mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Merge branch 'message_creator_ram' into integration
This commit is contained in:
		| @@ -12,7 +12,7 @@ repos: | ||||
|       # Run the formatter. | ||||
|       - id: ruff-format | ||||
|   - repo: https://github.com/PyCQA/flake8 | ||||
|     rev: 7.2.0 | ||||
|     rev: 7.3.0 | ||||
|     hooks: | ||||
|       - id: flake8 | ||||
|         additional_dependencies: | ||||
|   | ||||
| @@ -323,6 +323,7 @@ esphome/components/one_wire/* @ssieb | ||||
| esphome/components/online_image/* @clydebarrow @guillempages | ||||
| esphome/components/opentherm/* @olegtarasov | ||||
| esphome/components/openthread/* @mrene | ||||
| esphome/components/opt3001/* @ccutrer | ||||
| esphome/components/ota/* @esphome/core | ||||
| esphome/components/output/* @esphome/core | ||||
| esphome/components/packet_transport/* @clydebarrow | ||||
|   | ||||
| @@ -14,8 +14,8 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@grahambrown11", "@hwstar"] | ||||
| 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( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -190,7 +193,7 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id( | ||||
|  | ||||
|  | ||||
| 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, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         await automation.build_automation(trigger, [], conf) | ||||
|   | ||||
| @@ -188,6 +188,17 @@ message DeviceInfoRequest { | ||||
|   // 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 { | ||||
|   option (id) = 10; | ||||
|   option (source) = SOURCE_SERVER; | ||||
| @@ -236,6 +247,12 @@ message DeviceInfoResponse { | ||||
|  | ||||
|   // Supports receiving and saving api encryption key | ||||
|   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 { | ||||
| @@ -280,6 +297,7 @@ message ListEntitiesBinarySensorResponse { | ||||
|   bool disabled_by_default = 7; | ||||
|   string icon = 8; | ||||
|   EntityCategory entity_category = 9; | ||||
|   uint32 device_id = 10; | ||||
| } | ||||
| message BinarySensorStateResponse { | ||||
|   option (id) = 21; | ||||
| @@ -315,6 +333,7 @@ message ListEntitiesCoverResponse { | ||||
|   string icon = 10; | ||||
|   EntityCategory entity_category = 11; | ||||
|   bool supports_stop = 12; | ||||
|   uint32 device_id = 13; | ||||
| } | ||||
|  | ||||
| enum LegacyCoverState { | ||||
| @@ -388,6 +407,7 @@ message ListEntitiesFanResponse { | ||||
|   string icon = 10; | ||||
|   EntityCategory entity_category = 11; | ||||
|   repeated string supported_preset_modes = 12; | ||||
|   uint32 device_id = 13; | ||||
| } | ||||
| enum FanSpeed { | ||||
|   FAN_SPEED_LOW = 0; | ||||
| @@ -471,6 +491,7 @@ message ListEntitiesLightResponse { | ||||
|   bool disabled_by_default = 13; | ||||
|   string icon = 14; | ||||
|   EntityCategory entity_category = 15; | ||||
|   uint32 device_id = 16; | ||||
| } | ||||
| message LightStateResponse { | ||||
|   option (id) = 24; | ||||
| @@ -563,6 +584,7 @@ message ListEntitiesSensorResponse { | ||||
|   SensorLastResetType legacy_last_reset_type = 11; | ||||
|   bool disabled_by_default = 12; | ||||
|   EntityCategory entity_category = 13; | ||||
|   uint32 device_id = 14; | ||||
| } | ||||
| message SensorStateResponse { | ||||
|   option (id) = 25; | ||||
| @@ -595,6 +617,7 @@ message ListEntitiesSwitchResponse { | ||||
|   bool disabled_by_default = 7; | ||||
|   EntityCategory entity_category = 8; | ||||
|   string device_class = 9; | ||||
|   uint32 device_id = 10; | ||||
| } | ||||
| message SwitchStateResponse { | ||||
|   option (id) = 26; | ||||
| @@ -632,6 +655,7 @@ message ListEntitiesTextSensorResponse { | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   string device_class = 8; | ||||
|   uint32 device_id = 9; | ||||
| } | ||||
| message TextSensorStateResponse { | ||||
|   option (id) = 27; | ||||
| @@ -814,6 +838,7 @@ message ListEntitiesCameraResponse { | ||||
|   bool disabled_by_default = 5; | ||||
|   string icon = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   uint32 device_id = 8; | ||||
| } | ||||
|  | ||||
| message CameraImageResponse { | ||||
| @@ -916,6 +941,7 @@ message ListEntitiesClimateResponse { | ||||
|   bool supports_target_humidity = 23; | ||||
|   float visual_min_humidity = 24; | ||||
|   float visual_max_humidity = 25; | ||||
|   uint32 device_id = 26; | ||||
| } | ||||
| message ClimateStateResponse { | ||||
|   option (id) = 47; | ||||
| @@ -999,6 +1025,7 @@ message ListEntitiesNumberResponse { | ||||
|   string unit_of_measurement = 11; | ||||
|   NumberMode mode = 12; | ||||
|   string device_class = 13; | ||||
|   uint32 device_id = 14; | ||||
| } | ||||
| message NumberStateResponse { | ||||
|   option (id) = 50; | ||||
| @@ -1039,6 +1066,7 @@ message ListEntitiesSelectResponse { | ||||
|   repeated string options = 6; | ||||
|   bool disabled_by_default = 7; | ||||
|   EntityCategory entity_category = 8; | ||||
|   uint32 device_id = 9; | ||||
| } | ||||
| message SelectStateResponse { | ||||
|   option (id) = 53; | ||||
| @@ -1081,6 +1109,7 @@ message ListEntitiesSirenResponse { | ||||
|   bool supports_duration = 8; | ||||
|   bool supports_volume = 9; | ||||
|   EntityCategory entity_category = 10; | ||||
|   uint32 device_id = 11; | ||||
| } | ||||
| message SirenStateResponse { | ||||
|   option (id) = 56; | ||||
| @@ -1144,6 +1173,7 @@ message ListEntitiesLockResponse { | ||||
|  | ||||
|   // Not yet implemented: | ||||
|   string code_format = 11; | ||||
|   uint32 device_id = 12; | ||||
| } | ||||
| message LockStateResponse { | ||||
|   option (id) = 59; | ||||
| @@ -1183,6 +1213,7 @@ message ListEntitiesButtonResponse { | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   string device_class = 8; | ||||
|   uint32 device_id = 9; | ||||
| } | ||||
| message ButtonCommandRequest { | ||||
|   option (id) = 62; | ||||
| @@ -1238,6 +1269,8 @@ message ListEntitiesMediaPlayerResponse { | ||||
|   bool supports_pause = 8; | ||||
|  | ||||
|   repeated MediaPlayerSupportedFormat supported_formats = 9; | ||||
|  | ||||
|   uint32 device_id = 10; | ||||
| } | ||||
| message MediaPlayerStateResponse { | ||||
|   option (id) = 64; | ||||
| @@ -1778,6 +1811,7 @@ message ListEntitiesAlarmControlPanelResponse { | ||||
|   uint32 supported_features = 8; | ||||
|   bool requires_code = 9; | ||||
|   bool requires_code_to_arm = 10; | ||||
|   uint32 device_id = 11; | ||||
| } | ||||
|  | ||||
| message AlarmControlPanelStateResponse { | ||||
| @@ -1823,6 +1857,7 @@ message ListEntitiesTextResponse { | ||||
|   uint32 max_length = 9; | ||||
|   string pattern = 10; | ||||
|   TextMode mode = 11; | ||||
|   uint32 device_id = 12; | ||||
| } | ||||
| message TextStateResponse { | ||||
|   option (id) = 98; | ||||
| @@ -1863,6 +1898,7 @@ message ListEntitiesDateResponse { | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   uint32 device_id = 8; | ||||
| } | ||||
| message DateStateResponse { | ||||
|   option (id) = 101; | ||||
| @@ -1906,6 +1942,7 @@ message ListEntitiesTimeResponse { | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   uint32 device_id = 8; | ||||
| } | ||||
| message TimeStateResponse { | ||||
|   option (id) = 104; | ||||
| @@ -1952,6 +1989,7 @@ message ListEntitiesEventResponse { | ||||
|   string device_class = 8; | ||||
|  | ||||
|   repeated string event_types = 9; | ||||
|   uint32 device_id = 10; | ||||
| } | ||||
| message EventResponse { | ||||
|   option (id) = 108; | ||||
| @@ -1983,6 +2021,7 @@ message ListEntitiesValveResponse { | ||||
|   bool assumed_state = 9; | ||||
|   bool supports_position = 10; | ||||
|   bool supports_stop = 11; | ||||
|   uint32 device_id = 12; | ||||
| } | ||||
|  | ||||
| enum ValveOperation { | ||||
| @@ -2029,6 +2068,7 @@ message ListEntitiesDateTimeResponse { | ||||
|   string icon = 5; | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   uint32 device_id = 8; | ||||
| } | ||||
| message DateTimeStateResponse { | ||||
|   option (id) = 113; | ||||
| @@ -2069,6 +2109,7 @@ message ListEntitiesUpdateResponse { | ||||
|   bool disabled_by_default = 6; | ||||
|   EntityCategory entity_category = 7; | ||||
|   string device_class = 8; | ||||
|   uint32 device_id = 9; | ||||
| } | ||||
| message UpdateStateResponse { | ||||
|   option (id) = 117; | ||||
|   | ||||
| @@ -1440,7 +1440,7 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe | ||||
|  | ||||
| #ifdef USE_EVENT | ||||
| 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) { | ||||
|   this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE); | ||||
| @@ -1629,6 +1629,23 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { | ||||
| #endif | ||||
| #ifdef USE_API_NOISE | ||||
|   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 | ||||
|   return resp; | ||||
| } | ||||
| @@ -1778,7 +1795,8 @@ void APIConnection::process_batch_() { | ||||
|     const auto &item = this->deferred_batch_.items[0]; | ||||
|  | ||||
|     // 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 && | ||||
|         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) { | ||||
|     // Try to encode message | ||||
|     // 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) { | ||||
|       // 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, | ||||
|                                                    bool is_single) const { | ||||
|   switch (message_type_) { | ||||
|     case 0:  // Function pointer | ||||
|       return data_.ptr(entity, conn, remaining_size, is_single); | ||||
|  | ||||
|                                                    bool is_single, uint16_t message_type) const { | ||||
|   if (is_string()) { | ||||
|     // Handle string-based messages | ||||
|     switch (message_type) { | ||||
| #ifdef USE_EVENT | ||||
|     case EventResponse::MESSAGE_TYPE: { | ||||
|       auto *e = static_cast<event::Event *>(entity); | ||||
|       return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); | ||||
|     } | ||||
|       case EventResponse::MESSAGE_TYPE: { | ||||
|         auto *e = static_cast<event::Event *>(entity); | ||||
|         return APIConnection::try_send_event_response(e, *get_string_ptr(), conn, remaining_size, is_single); | ||||
|       } | ||||
| #endif | ||||
|  | ||||
|     default: | ||||
|       // Should not happen, return 0 to indicate no message | ||||
|       return 0; | ||||
|       default: | ||||
|         // Should not happen, return 0 to indicate no message | ||||
|         return 0; | ||||
|     } | ||||
|   } else { | ||||
|     // Function pointer case | ||||
|     return data_.ptr(entity, conn, remaining_size, is_single); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -301,6 +301,9 @@ class APIConnection : public APIServerConnection { | ||||
|     response.icon = entity->get_icon(); | ||||
|     response.disabled_by_default = entity->is_disabled_by_default(); | ||||
|     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 | ||||
| @@ -480,55 +483,57 @@ class APIConnection : public APIServerConnection { | ||||
|   // Function pointer type for message encoding | ||||
|   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 { | ||||
|     // Ensure pointer alignment allows LSB tagging | ||||
|     static_assert(alignof(std::string *) > 1, "String pointer alignment must be > 1 for LSB tagging"); | ||||
|  | ||||
|    public: | ||||
|     // Constructor for function pointer (message_type = 0) | ||||
|     MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; } | ||||
|     // Constructor for function pointer | ||||
|     MessageCreator(MessageCreatorPtr ptr) { | ||||
|       // Function pointers are always aligned, so LSB is 0 | ||||
|       data_.ptr = ptr; | ||||
|     } | ||||
|  | ||||
|     // Constructor for string state capture | ||||
|     MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) { | ||||
|       data_.string_ptr = new std::string(value); | ||||
|     explicit MessageCreator(const std::string &str_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 | ||||
|     ~MessageCreator() { | ||||
|       // Clean up string data for string-based message types | ||||
|       if (uses_string_data_()) { | ||||
|         delete data_.string_ptr; | ||||
|       if (has_tagged_string_ptr()) { | ||||
|         delete get_string_ptr(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Copy constructor | ||||
|     MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) { | ||||
|       if (message_type_ == 0) { | ||||
|         data_.ptr = other.data_.ptr; | ||||
|       } else if (uses_string_data_()) { | ||||
|         data_.string_ptr = new std::string(*other.data_.string_ptr); | ||||
|     MessageCreator(const MessageCreator &other) { | ||||
|       if (other.has_tagged_string_ptr()) { | ||||
|         auto *str = new std::string(*other.get_string_ptr()); | ||||
|         data_.tagged = reinterpret_cast<uintptr_t>(str) | 1; | ||||
|       } else { | ||||
|         data_ = other.data_;  // For POD types | ||||
|         data_ = other.data_; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Move constructor | ||||
|     MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) { | ||||
|       other.message_type_ = 0;  // Reset other to function pointer type | ||||
|       other.data_.ptr = nullptr; | ||||
|     } | ||||
|     MessageCreator(MessageCreator &&other) noexcept : data_(other.data_) { other.data_.ptr = nullptr; } | ||||
|  | ||||
|     // Assignment operators (needed for batch deduplication) | ||||
|     MessageCreator &operator=(const MessageCreator &other) { | ||||
|       if (this != &other) { | ||||
|         // Clean up current string data if needed | ||||
|         if (uses_string_data_()) { | ||||
|           delete data_.string_ptr; | ||||
|         if (has_tagged_string_ptr()) { | ||||
|           delete get_string_ptr(); | ||||
|         } | ||||
|         // Copy new data | ||||
|         message_type_ = other.message_type_; | ||||
|         if (other.message_type_ == 0) { | ||||
|           data_.ptr = other.data_.ptr; | ||||
|         } else if (other.uses_string_data_()) { | ||||
|           data_.string_ptr = new std::string(*other.data_.string_ptr); | ||||
|         if (other.has_tagged_string_ptr()) { | ||||
|           auto *str = new std::string(*other.get_string_ptr()); | ||||
|           data_.tagged = reinterpret_cast<uintptr_t>(str) | 1; | ||||
|         } else { | ||||
|           data_ = other.data_; | ||||
|         } | ||||
| @@ -539,30 +544,32 @@ class APIConnection : public APIServerConnection { | ||||
|     MessageCreator &operator=(MessageCreator &&other) noexcept { | ||||
|       if (this != &other) { | ||||
|         // Clean up current string data if needed | ||||
|         if (uses_string_data_()) { | ||||
|           delete data_.string_ptr; | ||||
|         if (has_tagged_string_ptr()) { | ||||
|           delete get_string_ptr(); | ||||
|         } | ||||
|         // Move data | ||||
|         message_type_ = other.message_type_; | ||||
|         data_ = other.data_; | ||||
|         // Reset other to safe state | ||||
|         other.message_type_ = 0; | ||||
|         other.data_.ptr = nullptr; | ||||
|       } | ||||
|       return *this; | ||||
|     } | ||||
|  | ||||
|     // Call operator | ||||
|     uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const; | ||||
|     // Call operator - now accepts message_type as parameter | ||||
|     uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, | ||||
|                         uint16_t message_type) const; | ||||
|  | ||||
|    private: | ||||
|     // Helper to check if this message type uses heap-allocated strings | ||||
|     bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; } | ||||
|     union CreatorData { | ||||
|       MessageCreatorPtr ptr;    // 8 bytes | ||||
|       std::string *string_ptr;  // 8 bytes | ||||
|     } data_;                    // 8 bytes | ||||
|     uint16_t message_type_;     // 2 bytes (0 = function ptr, >0 = state capture) | ||||
|     // Check if this contains a string pointer | ||||
|     bool has_tagged_string_ptr() const { return (data_.tagged & 1) != 0; } | ||||
|  | ||||
|     // Get the actual string pointer (clears the tag bit) | ||||
|     std::string *get_string_ptr() const { return reinterpret_cast<std::string *>(data_.tagged & ~uintptr_t(1)); } | ||||
|  | ||||
|     union { | ||||
|       MessageCreatorPtr ptr; | ||||
|       uintptr_t tagged; | ||||
|     } data_;  // 4 bytes on 32-bit | ||||
|   }; | ||||
|  | ||||
|   // Generic batching mechanism for both state updates and entity info | ||||
|   | ||||
| @@ -812,6 +812,103 @@ void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {} | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } | ||||
| #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) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
| @@ -896,6 +993,18 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v | ||||
|       this->bluetooth_mac_address = value.as_string(); | ||||
|       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: | ||||
|       return false; | ||||
|   } | ||||
| @@ -920,6 +1029,13 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(16, this->suggested_area); | ||||
|   buffer.encode_string(18, this->bluetooth_mac_address); | ||||
|   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 { | ||||
|   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->bluetooth_mac_address, 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 | ||||
| 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(YESNO(this->api_encryption_supported)); | ||||
|   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("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -1052,6 +1187,10 @@ bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVar | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -1102,6 +1241,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(7, this->disabled_by_default); | ||||
|   buffer.encode_string(8, this->icon); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -1236,6 +1382,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->supports_stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 13: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -1289,6 +1439,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(10, this->icon); | ||||
|   buffer.encode_enum<enums::EntityCategory>(11, this->entity_category); | ||||
|   buffer.encode_bool(12, this->supports_stop); | ||||
|   buffer.encode_uint32(13, this->device_id); | ||||
| } | ||||
| void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(YESNO(this->supports_stop)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -1565,6 +1722,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 13: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -1620,6 +1781,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   for (auto &it : this->supported_preset_modes) { | ||||
|     buffer.encode_string(12, it, true); | ||||
|   } | ||||
|   buffer.encode_uint32(13, this->device_id); | ||||
| } | ||||
| void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("\n"); | ||||
|   } | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -1987,6 +2155,10 @@ bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 16: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -2055,6 +2227,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(13, this->disabled_by_default); | ||||
|   buffer.encode_string(14, this->icon); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 2, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -2658,6 +2837,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 14: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       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_bool(12, this->disabled_by_default); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -2860,6 +3050,10 @@ bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -2910,6 +3104,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(7, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(8, this->entity_category); | ||||
|   buffer.encode_string(9, this->device_class); | ||||
|   buffer.encode_uint32(10, this->device_id); | ||||
| } | ||||
| void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -3061,6 +3262,10 @@ bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarIn | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -3110,6 +3315,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   buffer.encode_uint32(9, this->device_id); | ||||
| } | ||||
| void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -3922,6 +4134,10 @@ bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -3966,6 +4182,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(5, this->disabled_by_default); | ||||
|   buffer.encode_string(6, this->icon); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -4156,6 +4379,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v | ||||
|       this->supports_target_humidity = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 26: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -4262,6 +4489,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(23, this->supports_target_humidity); | ||||
|   buffer.encode_float(24, this->visual_min_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 { | ||||
|   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_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_uint32_field(total_size, 2, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -4901,6 +5135,10 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->mode = value.as_enum<enums::NumberMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 14: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -4971,6 +5209,7 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(11, this->unit_of_measurement); | ||||
|   buffer.encode_enum<enums::NumberMode>(12, this->mode); | ||||
|   buffer.encode_string(13, this->device_class); | ||||
|   buffer.encode_uint32(14, this->device_id); | ||||
| } | ||||
| void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -5151,6 +5396,10 @@ bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -5202,6 +5451,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   } | ||||
|   buffer.encode_bool(7, this->disabled_by_default); | ||||
|   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 { | ||||
|   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_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 | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -5378,6 +5634,10 @@ bool ListEntitiesSirenResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 11: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -5431,6 +5691,7 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(8, this->supports_duration); | ||||
|   buffer.encode_bool(9, this->supports_volume); | ||||
|   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 { | ||||
|   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_volume, 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 | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -5683,6 +5950,10 @@ bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt valu | ||||
|       this->requires_code = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 12: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -5735,6 +6006,7 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(9, this->supports_open); | ||||
|   buffer.encode_bool(10, this->requires_code); | ||||
|   buffer.encode_string(11, this->code_format); | ||||
|   buffer.encode_uint32(12, this->device_id); | ||||
| } | ||||
| void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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->requires_code, 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 | ||||
| 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("'").append(this->code_format).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -5922,6 +6200,10 @@ bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -5971,6 +6253,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   buffer.encode_uint32(9, this->device_id); | ||||
| } | ||||
| void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -6135,6 +6424,10 @@ bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarI | ||||
|       this->supports_pause = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -6187,6 +6480,7 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   for (auto &it : this->supported_formats) { | ||||
|     buffer.encode_message<MediaPlayerSupportedFormat>(9, it, true); | ||||
|   } | ||||
|   buffer.encode_uint32(10, this->device_id); | ||||
| } | ||||
| void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_bool_field(total_size, 1, this->supports_pause, false); | ||||
|   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 | ||||
| void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { | ||||
| @@ -6241,6 +6536,11 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { | ||||
|     it.dump_to(out); | ||||
|     out.append("\n"); | ||||
|   } | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -8551,6 +8851,10 @@ bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, Pro | ||||
|       this->requires_code_to_arm = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 11: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -8598,6 +8902,7 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) cons | ||||
|   buffer.encode_uint32(8, this->supported_features); | ||||
|   buffer.encode_bool(9, this->requires_code); | ||||
|   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 { | ||||
|   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_bool_field(total_size, 1, this->requires_code, 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 | ||||
| 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(YESNO(this->requires_code_to_arm)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -8783,6 +9094,10 @@ bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt valu | ||||
|       this->mode = value.as_enum<enums::TextMode>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 12: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -8835,6 +9150,7 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_uint32(9, this->max_length); | ||||
|   buffer.encode_string(10, this->pattern); | ||||
|   buffer.encode_enum<enums::TextMode>(11, this->mode); | ||||
|   buffer.encode_uint32(12, this->device_id); | ||||
| } | ||||
| void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_string_field(total_size, 1, this->pattern, 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 | ||||
| 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(proto_enum_to_string<enums::TextMode>(this->mode)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -9014,6 +9336,10 @@ bool ListEntitiesDateResponse::decode_varint(uint32_t field_id, ProtoVarInt valu | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -9058,6 +9384,7 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -9255,6 +9588,10 @@ bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt valu | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -9299,6 +9636,7 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -9496,6 +9840,10 @@ bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 10: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -9552,6 +9900,7 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   for (auto &it : this->event_types) { | ||||
|     buffer.encode_string(9, it, true); | ||||
|   } | ||||
|   buffer.encode_uint32(10, this->device_id); | ||||
| } | ||||
| void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("\n"); | ||||
|   } | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -9678,6 +10033,10 @@ bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt val | ||||
|       this->supports_stop = value.as_bool(); | ||||
|       return true; | ||||
|     } | ||||
|     case 12: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -9730,6 +10089,7 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(9, this->assumed_state); | ||||
|   buffer.encode_bool(10, this->supports_position); | ||||
|   buffer.encode_bool(11, this->supports_stop); | ||||
|   buffer.encode_uint32(12, this->device_id); | ||||
| } | ||||
| void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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->supports_position, 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 | ||||
| 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(YESNO(this->supports_stop)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -9923,6 +10289,10 @@ bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 8: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -9967,6 +10337,7 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_string(5, this->icon); | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   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 { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -10114,6 +10491,10 @@ bool ListEntitiesUpdateResponse::decode_varint(uint32_t field_id, ProtoVarInt va | ||||
|       this->entity_category = value.as_enum<enums::EntityCategory>(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->device_id = value.as_uint32(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -10163,6 +10544,7 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(6, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); | ||||
|   buffer.encode_string(8, this->device_class); | ||||
|   buffer.encode_uint32(9, this->device_id); | ||||
| } | ||||
| void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const { | ||||
|   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_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_uint32_field(total_size, 1, this->device_id, false); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_id: "); | ||||
|   sprintf(buffer, "%" PRIu32, this->device_id); | ||||
|   out.append(buffer); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -264,6 +264,7 @@ class InfoResponseProtoMessage : public ProtoMessage { | ||||
|   bool disabled_by_default{false}; | ||||
|   std::string icon{}; | ||||
|   enums::EntityCategory entity_category{}; | ||||
|   uint32_t device_id{0}; | ||||
|  | ||||
|  protected: | ||||
| }; | ||||
| @@ -415,10 +416,39 @@ class DeviceInfoRequest : public ProtoMessage { | ||||
|  | ||||
|  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 { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "device_info_response"; } | ||||
| #endif | ||||
| @@ -441,6 +471,9 @@ class DeviceInfoResponse : public ProtoMessage { | ||||
|   std::string suggested_area{}; | ||||
|   std::string bluetooth_mac_address{}; | ||||
|   bool api_encryption_supported{false}; | ||||
|   std::vector<DeviceInfo> devices{}; | ||||
|   std::vector<AreaInfo> areas{}; | ||||
|   AreaInfo area{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
|   void calculate_size(uint32_t &total_size) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| @@ -493,7 +526,7 @@ class SubscribeStatesRequest : public ProtoMessage { | ||||
| class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; } | ||||
| #endif | ||||
| @@ -532,7 +565,7 @@ class BinarySensorStateResponse : public StateResponseProtoMessage { | ||||
| class ListEntitiesCoverResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_cover_response"; } | ||||
| #endif | ||||
| @@ -601,7 +634,7 @@ class CoverCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesFanResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_fan_response"; } | ||||
| #endif | ||||
| @@ -679,7 +712,7 @@ class FanCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesLightResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_light_response"; } | ||||
| #endif | ||||
| @@ -780,7 +813,7 @@ class LightCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesSensorResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_sensor_response"; } | ||||
| #endif | ||||
| @@ -823,7 +856,7 @@ class SensorStateResponse : public StateResponseProtoMessage { | ||||
| class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_switch_response"; } | ||||
| #endif | ||||
| @@ -880,7 +913,7 @@ class SwitchCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_text_sensor_response"; } | ||||
| #endif | ||||
| @@ -1196,7 +1229,7 @@ class ExecuteServiceRequest : public ProtoMessage { | ||||
| class ListEntitiesCameraResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_camera_response"; } | ||||
| #endif | ||||
| @@ -1253,7 +1286,7 @@ class CameraImageRequest : public ProtoMessage { | ||||
| class ListEntitiesClimateResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_climate_response"; } | ||||
| #endif | ||||
| @@ -1362,7 +1395,7 @@ class ClimateCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesNumberResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_number_response"; } | ||||
| #endif | ||||
| @@ -1423,7 +1456,7 @@ class NumberCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesSelectResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_select_response"; } | ||||
| #endif | ||||
| @@ -1481,7 +1514,7 @@ class SelectCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesSirenResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_siren_response"; } | ||||
| #endif | ||||
| @@ -1547,7 +1580,7 @@ class SirenCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesLockResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_lock_response"; } | ||||
| #endif | ||||
| @@ -1609,7 +1642,7 @@ class LockCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesButtonResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_button_response"; } | ||||
| #endif | ||||
| @@ -1662,7 +1695,7 @@ class MediaPlayerSupportedFormat : public ProtoMessage { | ||||
| class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_media_player_response"; } | ||||
| #endif | ||||
| @@ -2532,7 +2565,7 @@ class VoiceAssistantSetConfiguration : public ProtoMessage { | ||||
| class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; } | ||||
| #endif | ||||
| @@ -2592,7 +2625,7 @@ class AlarmControlPanelCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesTextResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_text_response"; } | ||||
| #endif | ||||
| @@ -2653,7 +2686,7 @@ class TextCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesDateResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_date_response"; } | ||||
| #endif | ||||
| @@ -2713,7 +2746,7 @@ class DateCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesTimeResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_time_response"; } | ||||
| #endif | ||||
| @@ -2773,7 +2806,7 @@ class TimeCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesEventResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_event_response"; } | ||||
| #endif | ||||
| @@ -2811,7 +2844,7 @@ class EventResponse : public StateResponseProtoMessage { | ||||
| class ListEntitiesValveResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_valve_response"; } | ||||
| #endif | ||||
| @@ -2873,7 +2906,7 @@ class ValveCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_date_time_response"; } | ||||
| #endif | ||||
| @@ -2928,7 +2961,7 @@ class DateTimeCommandRequest : public ProtoMessage { | ||||
| class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { | ||||
|  public: | ||||
|   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 | ||||
|   static constexpr const char *message_name() { return "list_entities_update_response"; } | ||||
| #endif | ||||
|   | ||||
| @@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) { | ||||
| bool AudioTransferBuffer::allocate_buffer_(size_t 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_); | ||||
|   if (this->buffer_ == nullptr) { | ||||
| @@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { | ||||
|  | ||||
| void AudioTransferBuffer::deallocate_buffer_() { | ||||
|   if (this->buffer_ != nullptr) { | ||||
|     RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|     RAMAllocator<uint8_t> allocator; | ||||
|     allocator.deallocate(this->buffer_, this->buffer_size_); | ||||
|     this->buffer_ = nullptr; | ||||
|     this->data_start_ = nullptr; | ||||
|   | ||||
| @@ -7,11 +7,13 @@ | ||||
|  | ||||
| extern "C" { | ||||
| #include "rtos_pub.h" | ||||
| #include "spi.h" | ||||
| // rtos_pub.h must be included before the rest of the includes | ||||
|  | ||||
| #include "arm_arch.h" | ||||
| #include "general_dma_pub.h" | ||||
| #include "gpio_pub.h" | ||||
| #include "icu_pub.h" | ||||
| #include "spi.h" | ||||
| #undef SPI_DAT | ||||
| #undef SPI_BASE | ||||
| }; | ||||
| @@ -124,7 +126,7 @@ void BekenSPILEDStripLightOutput::setup() { | ||||
|   size_t buffer_size = this->get_buffer_size_(); | ||||
|   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); | ||||
|   if (this->buf_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Cannot allocate LED buffer!"); | ||||
|   | ||||
| @@ -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) | ||||
|   uint8_t turn_on = BH1750_COMMAND_POWER_ON; | ||||
|   if (this->write(&turn_on, 1) != i2c::ERROR_OK) { | ||||
|     ESP_LOGW(TAG, "Turning on BH1750 failed"); | ||||
|     ESP_LOGW(TAG, "Power on failed"); | ||||
|     f(NAN); | ||||
|     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_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) { | ||||
|       ESP_LOGW(TAG, "Setting measurement time for BH1750 failed"); | ||||
|       ESP_LOGW(TAG, "Set measurement time failed"); | ||||
|       active_mtreg_ = 0; | ||||
|       f(NAN); | ||||
|       return; | ||||
| @@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function< | ||||
|       return; | ||||
|   } | ||||
|   if (this->write(&cmd, 1) != i2c::ERROR_OK) { | ||||
|     ESP_LOGW(TAG, "Starting measurement for BH1750 failed"); | ||||
|     ESP_LOGW(TAG, "Start measurement failed"); | ||||
|     f(NAN); | ||||
|     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]() { | ||||
|     uint16_t raw_value; | ||||
|     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); | ||||
|       return; | ||||
|     } | ||||
| @@ -156,7 +156,7 @@ void BH1750Sensor::update() { | ||||
|         this->publish_state(NAN); | ||||
|         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->publish_state(val); | ||||
|     }); | ||||
|   | ||||
| @@ -60,8 +60,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_WINDOW, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
| from esphome.util import Registry | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| @@ -148,6 +148,7 @@ BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Conditi | ||||
|  | ||||
| # Filters | ||||
| Filter = binary_sensor_ns.class_("Filter") | ||||
| TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component) | ||||
| DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) | ||||
| DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", 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) | ||||
|  | ||||
|  | ||||
| @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( | ||||
|     "delayed_on_off", | ||||
|     DelayedOnOffFilter, | ||||
| @@ -491,6 +505,9 @@ _BINARY_SENSOR_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor")) | ||||
|  | ||||
|  | ||||
| def binary_sensor_schema( | ||||
|     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): | ||||
|     await setup_entity(var, config) | ||||
|     await setup_entity(var, config, "binary_sensor") | ||||
|  | ||||
|     if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|   | ||||
| @@ -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) { | ||||
|   if (value) { | ||||
|     this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); }); | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class Filter { | ||||
|  public: | ||||
|   virtual optional<bool> new_value(bool value) = 0; | ||||
|  | ||||
|   void input(bool value); | ||||
|   virtual void input(bool value); | ||||
|  | ||||
|   void output(bool value); | ||||
|  | ||||
| @@ -28,6 +28,16 @@ class Filter { | ||||
|   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 { | ||||
|  public: | ||||
|   optional<bool> new_value(bool value) override; | ||||
|   | ||||
| @@ -18,8 +18,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_UPDATE, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -61,6 +61,9 @@ _BUTTON_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _BUTTON_SCHEMA.add_extra(entity_duplicate_validator("button")) | ||||
|  | ||||
|  | ||||
| def button_schema( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -87,7 +90,7 @@ BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button")) | ||||
|  | ||||
|  | ||||
| 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, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|   | ||||
| @@ -48,8 +48,8 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| @@ -247,6 +247,9 @@ _CLIMATE_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _CLIMATE_SCHEMA.add_extra(entity_duplicate_validator("climate")) | ||||
|  | ||||
|  | ||||
| def climate_schema( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -273,7 +276,7 @@ CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate")) | ||||
|  | ||||
|  | ||||
| async def setup_climate_core_(var, config): | ||||
|     await setup_entity(var, config) | ||||
|     await setup_entity(var, config, "climate") | ||||
|  | ||||
|     visual = config[CONF_VISUAL] | ||||
|     if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: | ||||
|   | ||||
| @@ -33,8 +33,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_WINDOW, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| @@ -126,6 +126,9 @@ _COVER_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _COVER_SCHEMA.add_extra(entity_duplicate_validator("cover")) | ||||
|  | ||||
|  | ||||
| def cover_schema( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -154,7 +157,7 @@ COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover")) | ||||
|  | ||||
|  | ||||
| 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: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|   | ||||
| @@ -22,8 +22,8 @@ from esphome.const import ( | ||||
|     CONF_YEAR, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@rfdarter", "@jesserockz"] | ||||
|  | ||||
| @@ -84,6 +84,8 @@ _DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( | ||||
|     .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) | ||||
| ).add_extra(_validate_time_present) | ||||
|  | ||||
| _DATETIME_SCHEMA.add_extra(entity_duplicate_validator("datetime")) | ||||
|  | ||||
|  | ||||
| def date_schema(class_: MockObjClass) -> cv.Schema: | ||||
|     schema = cv.Schema( | ||||
| @@ -133,7 +135,7 @@ def datetime_schema(class_: MockObjClass) -> cv.Schema: | ||||
|  | ||||
|  | ||||
| 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: | ||||
|         mqtt_ = cg.new_Pvariable(mqtt_id, var) | ||||
|   | ||||
| @@ -455,7 +455,7 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|                     CONF_NAME: "Demo Plain Sensor", | ||||
|                 }, | ||||
|                 { | ||||
|                     CONF_NAME: "Demo Temperature Sensor", | ||||
|                     CONF_NAME: "Demo Temperature Sensor 1", | ||||
|                     CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS, | ||||
|                     CONF_ICON: ICON_THERMOMETER, | ||||
|                     CONF_ACCURACY_DECIMALS: 1, | ||||
| @@ -463,7 +463,7 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|                     CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT, | ||||
|                 }, | ||||
|                 { | ||||
|                     CONF_NAME: "Demo Temperature Sensor", | ||||
|                     CONF_NAME: "Demo Temperature Sensor 2", | ||||
|                     CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS, | ||||
|                     CONF_ICON: ICON_THERMOMETER, | ||||
|                     CONF_ACCURACY_DECIMALS: 1, | ||||
|   | ||||
| @@ -11,7 +11,7 @@ namespace display { | ||||
| static const char *const TAG = "display"; | ||||
|  | ||||
| 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); | ||||
|   if (this->buffer_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Could not allocate buffer for display!"); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "ble_event_pool.h" | ||||
|  | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include <esp_bt.h> | ||||
| @@ -516,13 +517,12 @@ void ESP32BLE::dump_config() { | ||||
|         break; | ||||
|     } | ||||
|     ESP_LOGCONFIG(TAG, | ||||
|                   "ESP32 BLE:\n" | ||||
|                   "  MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" | ||||
|                   "BLE:\n" | ||||
|                   "  MAC address: %s\n" | ||||
|                   "  IO Capability: %s", | ||||
|                   mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5], | ||||
|                   io_capability_s); | ||||
|                   format_mac_address_pretty(mac_address).c_str(), io_capability_s); | ||||
|   } else { | ||||
|     ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); | ||||
|     ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled"); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData | ||||
| } | ||||
|  | ||||
| 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++) | ||||
|     this->address_[i] = scan_result.bda[i]; | ||||
|   this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type); | ||||
|   | ||||
| @@ -85,6 +85,9 @@ class ESPBTDevice { | ||||
|  | ||||
|   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; | ||||
|  | ||||
|   optional<ESPBLEiBeacon> get_ibeacon() const { | ||||
| @@ -111,6 +114,7 @@ class ESPBTDevice { | ||||
|   std::vector<ESPBTUUID> service_uuids_{}; | ||||
|   std::vector<ServiceData> manufacturer_datas_{}; | ||||
|   std::vector<ServiceData> service_datas_{}; | ||||
|   const BLEScanResult *scan_result_{nullptr}; | ||||
| }; | ||||
|  | ||||
| class ESP32BLETracker; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| from esphome import automation, pins | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import i2c | ||||
| from esphome.components.esp32 import add_idf_component | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
| @@ -7,6 +8,7 @@ from esphome.const import ( | ||||
|     CONF_CONTRAST, | ||||
|     CONF_DATA_PINS, | ||||
|     CONF_FREQUENCY, | ||||
|     CONF_I2C_ID, | ||||
|     CONF_ID, | ||||
|     CONF_PIN, | ||||
|     CONF_RESET_PIN, | ||||
| @@ -17,7 +19,7 @@ from esphome.const import ( | ||||
|     CONF_VSYNC_PIN, | ||||
| ) | ||||
| from esphome.core import CORE | ||||
| from esphome.cpp_helpers import setup_entity | ||||
| from esphome.core.entity_helpers import setup_entity | ||||
|  | ||||
| DEPENDENCIES = ["esp32"] | ||||
|  | ||||
| @@ -149,93 +151,104 @@ CONF_ON_IMAGE = "on_image" | ||||
|  | ||||
| camera_range_param = cv.int_range(min=-2, max=2) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( | ||||
|     { | ||||
|         cv.GenerateID(): cv.declare_id(ESP32Camera), | ||||
|         # pin assignment | ||||
|         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_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.frequency, cv.Range(min=8e6, max=20e6) | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Required(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.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, | ||||
|         cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, | ||||
|         # image | ||||
|         cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( | ||||
|             FRAME_SIZES, upper=True | ||||
|         ), | ||||
|         cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), | ||||
|         cv.Optional(CONF_CONTRAST, default=0): camera_range_param, | ||||
|         cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, | ||||
|         cv.Optional(CONF_SATURATION, default=0): camera_range_param, | ||||
|         cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, | ||||
|         cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, | ||||
|         cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( | ||||
|             ENUM_SPECIAL_EFFECT, upper=True | ||||
|         ), | ||||
|         # exposure | ||||
|         cv.Optional(CONF_AGC_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_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), | ||||
|         # gains | ||||
|         cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( | ||||
|             ENUM_GAIN_CONTROL_MODE, upper=True | ||||
|         ), | ||||
|         cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), | ||||
|         cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( | ||||
|             ENUM_GAIN_CEILING, upper=True | ||||
|         ), | ||||
|         # white balance | ||||
|         cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True), | ||||
|         # test pattern | ||||
|         cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, | ||||
|         # framerates | ||||
|         cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( | ||||
|             cv.framerate, cv.Range(min=0, min_included=False, max=60) | ||||
|         ), | ||||
|         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_START): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||
|                     ESP32CameraStreamStartTrigger | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||
|                     ESP32CameraStreamStopTrigger | ||||
|                 ), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional(CONF_ON_IMAGE): automation.validate_automation( | ||||
|             { | ||||
|                 cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger), | ||||
|             } | ||||
|         ), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.ENTITY_BASE_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(ESP32Camera), | ||||
|             # pin assignment | ||||
|             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_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.frequency, cv.Range(min=8e6, max=20e6) | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             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.Optional(CONF_I2C_ID): cv.Any( | ||||
|                 cv.use_id(i2c.InternalI2CBus), | ||||
|                 msg="I2C bus must be an internal ESP32 I2C bus", | ||||
|             ), | ||||
|             cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, | ||||
|             cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, | ||||
|             # image | ||||
|             cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( | ||||
|                 FRAME_SIZES, upper=True | ||||
|             ), | ||||
|             cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), | ||||
|             cv.Optional(CONF_CONTRAST, default=0): camera_range_param, | ||||
|             cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, | ||||
|             cv.Optional(CONF_SATURATION, default=0): camera_range_param, | ||||
|             cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, | ||||
|             cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, | ||||
|             cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( | ||||
|                 ENUM_SPECIAL_EFFECT, upper=True | ||||
|             ), | ||||
|             # exposure | ||||
|             cv.Optional(CONF_AGC_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_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), | ||||
|             # gains | ||||
|             cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( | ||||
|                 ENUM_GAIN_CONTROL_MODE, upper=True | ||||
|             ), | ||||
|             cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), | ||||
|             cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( | ||||
|                 ENUM_GAIN_CEILING, upper=True | ||||
|             ), | ||||
|             # white balance | ||||
|             cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum( | ||||
|                 ENUM_WB_MODE, upper=True | ||||
|             ), | ||||
|             # test pattern | ||||
|             cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, | ||||
|             # framerates | ||||
|             cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( | ||||
|                 cv.framerate, cv.Range(min=0, min_included=False, max=60) | ||||
|             ), | ||||
|             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_START): automation.validate_automation( | ||||
|                 { | ||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||
|                         ESP32CameraStreamStartTrigger | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( | ||||
|                 { | ||||
|                     cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||
|                         ESP32CameraStreamStopTrigger | ||||
|                     ), | ||||
|                 } | ||||
|             ), | ||||
|             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 = { | ||||
|     # pin assignment | ||||
| @@ -271,7 +284,7 @@ SETTERS = { | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await setup_entity(var, config) | ||||
|     await setup_entity(var, config, "camera") | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     for key, setter in SETTERS.items(): | ||||
| @@ -280,8 +293,12 @@ async def to_code(config): | ||||
|  | ||||
|     extclk = config[CONF_EXTERNAL_CLOCK] | ||||
|     cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY])) | ||||
|     i2c_pins = config[CONF_I2C_PINS] | ||||
|     cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL])) | ||||
|     if i2c_id := config.get(CONF_I2C_ID): | ||||
|         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])) | ||||
|     if config[CONF_IDLE_FRAMERATE] == 0: | ||||
|         cg.add(var.set_idle_update_interval(0)) | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include "esp32_camera.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include <freertos/task.h> | ||||
|  | ||||
| @@ -16,6 +16,12 @@ static const char *const TAG = "esp32_camera"; | ||||
| void ESP32Camera::setup() { | ||||
|   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 */ | ||||
|   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_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_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } | ||||
|  | ||||
|   | ||||
| @@ -2,13 +2,17 @@ | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <esp_camera.h> | ||||
| #include <freertos/FreeRTOS.h> | ||||
| #include <freertos/queue.h> | ||||
| #include "esphome/core/automation.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/entity_base.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include <esp_camera.h> | ||||
| #include <freertos/FreeRTOS.h> | ||||
| #include <freertos/queue.h> | ||||
|  | ||||
| #ifdef USE_I2C | ||||
| #include "esphome/components/i2c/i2c_bus.h" | ||||
| #endif  // USE_I2C | ||||
|  | ||||
| namespace esphome { | ||||
| namespace esp32_camera { | ||||
| @@ -118,6 +122,9 @@ class ESP32Camera : public EntityBase, public Component { | ||||
|   void set_pixel_clock_pin(uint8_t pin); | ||||
|   void set_external_clock(uint8_t pin, uint32_t frequency); | ||||
|   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_power_down_pin(uint8_t pin); | ||||
|   /* -- image */ | ||||
| @@ -210,6 +217,9 @@ class ESP32Camera : public EntityBase, public Component { | ||||
|  | ||||
|   uint32_t last_idle_request_{0}; | ||||
|   uint32_t last_update_{0}; | ||||
| #ifdef USE_I2C | ||||
|   i2c::InternalI2CBus *i2c_bus_{nullptr}; | ||||
| #endif  // USE_I2C | ||||
| }; | ||||
|  | ||||
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/esp32_hall/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/esp32_hall/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										5
									
								
								esphome/components/esp32_hall/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								esphome/components/esp32_hall/sensor.py
									
									
									
									
									
										Normal 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." | ||||
| ) | ||||
| @@ -18,8 +18,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_MOTION, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@nohat"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -59,6 +59,9 @@ _EVENT_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _EVENT_SCHEMA.add_extra(entity_duplicate_validator("event")) | ||||
|  | ||||
|  | ||||
| def event_schema( | ||||
|     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]): | ||||
|     await setup_entity(var, config) | ||||
|     await setup_entity(var, config, "event") | ||||
|  | ||||
|     for conf in config.get(CONF_ON_EVENT, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|   | ||||
| @@ -32,7 +32,7 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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 | ||||
|  | ||||
| @@ -161,6 +161,9 @@ _FAN_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _FAN_SCHEMA.add_extra(entity_duplicate_validator("fan")) | ||||
|  | ||||
|  | ||||
| def fan_schema( | ||||
|     class_: cg.Pvariable, | ||||
|     *, | ||||
| @@ -225,7 +228,7 @@ def validate_preset_modes(value): | ||||
|  | ||||
|  | ||||
| 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])) | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| from collections.abc import MutableMapping | ||||
| import functools | ||||
| import hashlib | ||||
| from itertools import accumulate | ||||
| import logging | ||||
| import os | ||||
| from pathlib import Path | ||||
| @@ -468,8 +469,9 @@ class EFont: | ||||
|  | ||||
|  | ||||
| class GlyphInfo: | ||||
|     def __init__(self, data_len, advance, offset_x, offset_y, width, height): | ||||
|         self.data_len = data_len | ||||
|     def __init__(self, glyph, data, advance, offset_x, offset_y, width, height): | ||||
|         self.glyph = glyph | ||||
|         self.bitmap_data = data | ||||
|         self.advance = advance | ||||
|         self.offset_x = offset_x | ||||
|         self.offset_y = offset_y | ||||
| @@ -477,6 +479,62 @@ class GlyphInfo: | ||||
|         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): | ||||
|     """ | ||||
|     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.sort(key=functools.cmp_to_key(glyph_comparator)) | ||||
|     glyph_args = {} | ||||
|     data = [] | ||||
|     bpp = config[CONF_BPP] | ||||
|     scale = 256 // (1 << bpp) | ||||
|     size = config[CONF_SIZE] | ||||
|     # create the data array for all glyphs | ||||
|     for codepoint in codepoints: | ||||
|         font = point_font_map[codepoint] | ||||
|         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(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] | ||||
|     glyph_args = [ | ||||
|         glyph_to_glyphinfo(x, point_font_map[x], size, bpp) for x in codepoints | ||||
|     ] | ||||
|     rhs = [HexInt(x) for x in flatten([x.bitmap_data for x in glyph_args])] | ||||
|     prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) | ||||
|  | ||||
|     # Create the glyph table that points to data in the above array. | ||||
|     glyph_initializer = [] | ||||
|     for codepoint in codepoints: | ||||
|         glyph_initializer.append( | ||||
|             cg.StructInitializer( | ||||
|                 GlyphData, | ||||
|                 ( | ||||
|                     "a_char", | ||||
|                     cg.RawExpression( | ||||
|                         f"(const uint8_t *){cpp_string_escape(codepoint)}" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ( | ||||
|                     "data", | ||||
|                     cg.RawExpression( | ||||
|                         f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}" | ||||
|                     ), | ||||
|                 ), | ||||
|                 ("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), | ||||
|             ) | ||||
|     glyph_initializer = [ | ||||
|         cg.StructInitializer( | ||||
|             GlyphData, | ||||
|             ( | ||||
|                 "a_char", | ||||
|                 cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"), | ||||
|             ), | ||||
|             ( | ||||
|                 "data", | ||||
|                 cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"), | ||||
|             ), | ||||
|             ("advance", x.advance), | ||||
|             ("offset_x", x.offset_x), | ||||
|             ("offset_y", x.offset_y), | ||||
|             ("width", x.width), | ||||
|             ("height", x.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) | ||||
|  | ||||
|     font_height = pt_to_px(base_font.size.height) | ||||
|     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 not base_font.is_scalable: | ||||
|             font_height = size | ||||
| @@ -610,5 +617,8 @@ async def to_code(config): | ||||
|         len(glyph_initializer), | ||||
|         ascender, | ||||
|         font_height, | ||||
|         descender, | ||||
|         xheight, | ||||
|         capheight, | ||||
|         bpp, | ||||
|     ) | ||||
|   | ||||
| @@ -45,8 +45,15 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const { | ||||
|   *height = this->glyph_data_->height; | ||||
| } | ||||
|  | ||||
| Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp) | ||||
|     : baseline_(baseline), height_(height), bpp_(bpp) { | ||||
| Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight, | ||||
|            uint8_t bpp) | ||||
|     : baseline_(baseline), | ||||
|       height_(height), | ||||
|       descender_(descender), | ||||
|       linegap_(height - baseline - descender), | ||||
|       xheight_(xheight), | ||||
|       capheight_(capheight), | ||||
|       bpp_(bpp) { | ||||
|   glyphs_.reserve(data_nr); | ||||
|   for (int i = 0; i < data_nr; ++i) | ||||
|     glyphs_.emplace_back(&data[i]); | ||||
|   | ||||
| @@ -50,11 +50,17 @@ class Font | ||||
|  public: | ||||
|   /** 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 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); | ||||
|  | ||||
| @@ -65,14 +71,23 @@ class Font | ||||
| #endif | ||||
|   inline int get_baseline() { return this->baseline_; } | ||||
|   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_; } | ||||
|  | ||||
|   const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; } | ||||
|   const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; } | ||||
|  | ||||
|  protected: | ||||
|   std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_; | ||||
|   std::vector<Glyph, RAMAllocator<Glyph>> glyphs_; | ||||
|   int baseline_; | ||||
|   int height_; | ||||
|   int descender_; | ||||
|   int linegap_; | ||||
|   int xheight_; | ||||
|   int capheight_; | ||||
|   uint8_t bpp_;  // bits per pixel | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -41,6 +41,6 @@ CONFIG_SCHEMA = cv.All( | ||||
| async def to_code(config): | ||||
|     cg.add_build_flag("-DUSE_HOST") | ||||
|     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_platformio_option("platform", "platformio/native") | ||||
|   | ||||
| @@ -239,7 +239,7 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> { | ||||
|  | ||||
|     std::string response_body; | ||||
|     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); | ||||
|       if (buf != nullptr) { | ||||
|         size_t read_index = 0; | ||||
|   | ||||
| @@ -54,7 +54,7 @@ void HttpRequestUpdate::update_task(void *params) { | ||||
|     UPDATE_RETURN; | ||||
|   } | ||||
|  | ||||
|   ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> allocator; | ||||
|   uint8_t *data = allocator.allocate(container->content_length); | ||||
|   if (data == nullptr) { | ||||
|     std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length); | ||||
|   | ||||
| @@ -22,8 +22,9 @@ import esphome.final_validate as fv | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| i2c_ns = cg.esphome_ns.namespace("i2c") | ||||
| I2CBus = i2c_ns.class_("I2CBus") | ||||
| ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component) | ||||
| IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component) | ||||
| InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus) | ||||
| ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component) | ||||
| IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component) | ||||
| I2CDevice = i2c_ns.class_("I2CDevice") | ||||
|  | ||||
|  | ||||
| @@ -71,6 +72,7 @@ CONFIG_SCHEMA = cv.All( | ||||
| @coroutine_with_priority(1.0) | ||||
| async def to_code(config): | ||||
|     cg.add_global(i2c_ns.using) | ||||
|     cg.add_define("USE_I2C") | ||||
|     var = cg.new_Pvariable(config[CONF_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #pragma once | ||||
| #include <cstdint> | ||||
| #include <cstddef> | ||||
| #include <cstdint> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| @@ -108,5 +108,12 @@ class I2CBus { | ||||
|   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 esphome | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| #ifdef USE_ARDUINO | ||||
|  | ||||
| #include "i2c_bus_arduino.h" | ||||
| #include <Arduino.h> | ||||
| #include <cstring> | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <Arduino.h> | ||||
| #include <cstring> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
| @@ -23,6 +23,7 @@ void ArduinoI2CBus::setup() { | ||||
|   } else { | ||||
|     wire_ = new TwoWire(next_bus_num);  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   } | ||||
|   this->port_ = next_bus_num; | ||||
|   next_bus_num++; | ||||
| #elif defined(USE_ESP8266) | ||||
|   wire_ = new TwoWire();  // NOLINT(cppcoreguidelines-owning-memory) | ||||
|   | ||||
| @@ -2,9 +2,9 @@ | ||||
|  | ||||
| #ifdef USE_ARDUINO | ||||
|  | ||||
| #include "i2c_bus.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include <Wire.h> | ||||
| #include "esphome/core/component.h" | ||||
| #include "i2c_bus.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
| @@ -15,7 +15,7 @@ enum RecoveryCode { | ||||
|   RECOVERY_COMPLETED, | ||||
| }; | ||||
|  | ||||
| class ArduinoI2CBus : public I2CBus, public Component { | ||||
| class ArduinoI2CBus : public InternalI2CBus, public Component { | ||||
|  public: | ||||
|   void setup() 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_timeout(uint32_t timeout) { timeout_ = timeout; } | ||||
|  | ||||
|   int get_port() const override { return this->port_; } | ||||
|  | ||||
|  private: | ||||
|   void recover_(); | ||||
|   void set_pins_and_clock_(); | ||||
|   RecoveryCode recovery_result_; | ||||
|  | ||||
|  protected: | ||||
|   int8_t port_{-1}; | ||||
|   TwoWire *wire_; | ||||
|   uint8_t sda_pin_; | ||||
|   uint8_t scl_pin_; | ||||
|   | ||||
| @@ -2,9 +2,9 @@ | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
|  | ||||
| #include "i2c_bus.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include <driver/i2c.h> | ||||
| #include "esphome/core/component.h" | ||||
| #include "i2c_bus.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
| @@ -15,7 +15,7 @@ enum RecoveryCode { | ||||
|   RECOVERY_COMPLETED, | ||||
| }; | ||||
|  | ||||
| class IDFI2CBus : public I2CBus, public Component { | ||||
| class IDFI2CBus : public InternalI2CBus, public Component { | ||||
|  public: | ||||
|   void setup() 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_timeout(uint32_t timeout) { timeout_ = timeout; } | ||||
|  | ||||
|   int get_port() const override { return static_cast<int>(this->port_); } | ||||
|  | ||||
|  private: | ||||
|   void recover_(); | ||||
|   RecoveryCode recovery_result_; | ||||
|   | ||||
| @@ -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) { | ||||
|   if (this->data_buffer_ == nullptr) { | ||||
|     // 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); | ||||
|   } | ||||
|  | ||||
| @@ -698,7 +698,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) { | ||||
|   this->audio_ring_buffer_.reset();  // Releases ownership of the shared_ptr | ||||
|  | ||||
|   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); | ||||
|     this->data_buffer_ = nullptr; | ||||
|   } | ||||
|   | ||||
| @@ -57,8 +57,8 @@ void Inkplate6::setup() { | ||||
|  * Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed. | ||||
|  */ | ||||
| void Inkplate6::initialize_() { | ||||
|   ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> allocator; | ||||
|   RAMAllocator<uint32_t> allocator32; | ||||
|   uint32_t buffer_size = this->get_buffer_length_(); | ||||
|   if (buffer_size == 0) | ||||
|     return; | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #endif | ||||
|  | ||||
| #include "esphome/core/application.h" | ||||
|  | ||||
| #define highbyte(val) (uint8_t)((val) >> 8) | ||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | ||||
|  | ||||
| @@ -73,9 +75,9 @@ void LD2410Component::dump_config() { | ||||
| #endif | ||||
|   this->read_all_info(); | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "  Throttle_ : %ums\n" | ||||
|                 "  MAC Address : %s\n" | ||||
|                 "  Firmware Version : %s", | ||||
|                 "  Throttle: %ums\n" | ||||
|                 "  MAC address: %s\n" | ||||
|                 "  Firmware version: %s", | ||||
|                 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 | ||||
|   */ | ||||
|   int32_t current_millis = millis(); | ||||
|   int32_t current_millis = App.get_loop_component_start_time(); | ||||
|   if (current_millis - last_periodic_millis_ < this->throttle_) | ||||
|     return; | ||||
|   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 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 | ||||
| std::function<void(void)> set_number_value(number::Number *n, float value) { | ||||
|   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) { | ||||
|   ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); | ||||
|   if (len < 10) { | ||||
|     ESP_LOGE(TAG, "Error with last command : incorrect length"); | ||||
|     ESP_LOGE(TAG, "Invalid length"); | ||||
|     return true; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|   if (buffer[COMMAND_STATUS] != 0x01) { | ||||
|     ESP_LOGE(TAG, "Error with last command : status != 0x01"); | ||||
|     ESP_LOGE(TAG, "Invalid status"); | ||||
|     return true; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|  | ||||
|   switch (buffer[COMMAND]) { | ||||
|     case lowbyte(CMD_ENABLE_CONF): | ||||
|       ESP_LOGV(TAG, "Handled Enable conf command"); | ||||
|       ESP_LOGV(TAG, "Enable conf"); | ||||
|       break; | ||||
|     case lowbyte(CMD_DISABLE_CONF): | ||||
|       ESP_LOGV(TAG, "Handled Disabled conf command"); | ||||
|       ESP_LOGV(TAG, "Disabled conf"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_BAUD_RATE): | ||||
|       ESP_LOGV(TAG, "Handled baud rate change command"); | ||||
|       ESP_LOGV(TAG, "Baud rate change"); | ||||
| #ifdef USE_SELECT | ||||
|       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 | ||||
|       break; | ||||
|     case lowbyte(CMD_VERSION): | ||||
|       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 | ||||
|       if (this->version_text_sensor_ != nullptr) { | ||||
|         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): { | ||||
|       std::string distance_resolution = | ||||
|           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 | ||||
|       if (this->distance_resolution_select_ != nullptr && | ||||
|           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_threshold_ = buffer[11] * 1.0; | ||||
|       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 threshold is: %f", this->light_threshold_); | ||||
|       ESP_LOGV(TAG, "Out pin level is: %s", const_cast<char *>(this->out_pin_level_.c_str())); | ||||
|       ESP_LOGV(TAG, "Light function: %s", const_cast<char *>(this->light_function_.c_str())); | ||||
|       ESP_LOGV(TAG, "Light threshold: %f", this->light_threshold_); | ||||
|       ESP_LOGV(TAG, "Out pin level: %s", const_cast<char *>(this->out_pin_level_.c_str())); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->light_function_select_ != nullptr && this->light_function_select_->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) { | ||||
|         return false; | ||||
|       } | ||||
|       this->mac_ = format_mac(buffer); | ||||
|       ESP_LOGV(TAG, "MAC Address is: %s", const_cast<char *>(this->mac_.c_str())); | ||||
|       this->mac_ = format_mac_address_pretty(&buffer[10]); | ||||
|       ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|       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 | ||||
| #ifdef USE_SWITCH | ||||
| @@ -420,19 +407,19 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_GATE_SENS): | ||||
|       ESP_LOGV(TAG, "Handled sensitivity command"); | ||||
|       ESP_LOGV(TAG, "Sensitivity"); | ||||
|       break; | ||||
|     case lowbyte(CMD_BLUETOOTH): | ||||
|       ESP_LOGV(TAG, "Handled bluetooth command"); | ||||
|       ESP_LOGV(TAG, "Bluetooth"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_DISTANCE_RESOLUTION): | ||||
|       ESP_LOGV(TAG, "Handled set distance resolution command"); | ||||
|       ESP_LOGV(TAG, "Set distance resolution"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_LIGHT_CONTROL): | ||||
|       ESP_LOGV(TAG, "Handled set light control command"); | ||||
|       ESP_LOGV(TAG, "Set light control"); | ||||
|       break; | ||||
|     case lowbyte(CMD_BT_PASSWORD): | ||||
|       ESP_LOGV(TAG, "Handled set bluetooth password command"); | ||||
|       ESP_LOGV(TAG, "Set bluetooth password"); | ||||
|       break; | ||||
|     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) { | ||||
|   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; | ||||
|   } | ||||
|   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) { | ||||
|   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; | ||||
|   this->send_command_(cmd, nullptr, 0); | ||||
|   this->set_config_mode_(false); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include "ld2420.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| /* | ||||
| @@ -40,7 +41,7 @@ There are three documented parameters for modes: | ||||
|   00 04 = Energy output mode | ||||
|     This mode outputs detailed signal energy values for each gate and the target distance. | ||||
|     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 | ||||
|     F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5 | ||||
|   00 00 = debug output mode | ||||
| @@ -67,10 +68,10 @@ float LD2420Component::get_setup_priority() const { return setup_priority::BUS; | ||||
| void LD2420Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "LD2420:\n" | ||||
|                 "  Firmware Version : %7s\n" | ||||
|                 "LD2420 Number:", | ||||
|                 "  Firmware version: %7s", | ||||
|                 this->ld2420_firmware_ver_); | ||||
| #ifdef USE_NUMBER | ||||
|   ESP_LOGCONFIG(TAG, "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 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, "  Restart Module:", this->restart_module_button_); | ||||
| #endif | ||||
|   ESP_LOGCONFIG(TAG, "LD2420 Select:"); | ||||
|   ESP_LOGCONFIG(TAG, "Select:"); | ||||
|   LOG_SELECT(TAG, "  Operating Mode", this->operating_selector_); | ||||
|   if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { | ||||
|     ESP_LOGW(TAG, "LD2420 Firmware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_); | ||||
|   if (LD2420Component::get_firmware_int(this->ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { | ||||
|     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; | ||||
| } | ||||
|  | ||||
| int LD2420Component::get_firmware_int_(const char *version_string) { | ||||
| int LD2420Component::get_firmware_int(const char *version_string) { | ||||
|   std::string version_str = version_string; | ||||
|   if (version_str[0] == 'v') { | ||||
|     version_str = version_str.substr(1); | ||||
| @@ -115,7 +116,7 @@ int LD2420Component::get_firmware_int_(const char *version_string) { | ||||
| void LD2420Component::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Running setup"); | ||||
|   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(); | ||||
|     return; | ||||
|   } | ||||
| @@ -127,7 +128,7 @@ void LD2420Component::setup() { | ||||
|   const char *pfw = this->ld2420_firmware_ver_; | ||||
|   std::string fw_str(pfw); | ||||
|  | ||||
|   for (auto &listener : listeners_) { | ||||
|   for (auto &listener : this->listeners_) { | ||||
|     listener->on_fw_version(fw_str); | ||||
|   } | ||||
|  | ||||
| @@ -137,11 +138,11 @@ void LD2420Component::setup() { | ||||
|   } | ||||
|  | ||||
|   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->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING); | ||||
|     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 { | ||||
|     this->set_mode_(CMD_SYSTEM_MODE_ENERGY); | ||||
|     this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING); | ||||
| @@ -151,18 +152,17 @@ void LD2420Component::setup() { | ||||
| #endif | ||||
|   this->set_system_mode(this->system_mode_); | ||||
|   this->set_config_mode(false); | ||||
|   ESP_LOGCONFIG(TAG, "LD2420 setup complete."); | ||||
| } | ||||
|  | ||||
| void LD2420Component::apply_config_action() { | ||||
|   const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_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; | ||||
|   } | ||||
|   ESP_LOGCONFIG(TAG, "Reconfiguring LD2420"); | ||||
|   ESP_LOGD(TAG, "Reconfiguring"); | ||||
|   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(); | ||||
|     return; | ||||
|   } | ||||
| @@ -178,13 +178,12 @@ void LD2420Component::apply_config_action() { | ||||
|   this->set_system_mode(this->system_mode_); | ||||
|   this->set_config_mode(false);  // Disable config mode to save new values in LD2420 nvm | ||||
|   this->set_operating_mode(OP_NORMAL_MODE_STRING); | ||||
|   ESP_LOGCONFIG(TAG, "LD2420 reconfig complete."); | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|     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(); | ||||
|     return; | ||||
|   } | ||||
| @@ -207,18 +206,16 @@ void LD2420Component::factory_reset_action() { | ||||
|   this->init_gate_config_numbers(); | ||||
|   this->refresh_gate_config_numbers(); | ||||
| #endif | ||||
|   ESP_LOGCONFIG(TAG, "LD2420 factory reset complete."); | ||||
| } | ||||
|  | ||||
| void LD2420Component::restart_module_action() { | ||||
|   ESP_LOGCONFIG(TAG, "Restarting LD2420 module"); | ||||
|   ESP_LOGD(TAG, "Restarting"); | ||||
|   this->send_module_restart(); | ||||
|   this->set_timeout(250, [this]() { | ||||
|     this->set_config_mode(true); | ||||
|     this->set_system_mode(system_mode_); | ||||
|     this->set_system_mode(this->system_mode_); | ||||
|     this->set_config_mode(false); | ||||
|   }); | ||||
|   ESP_LOGCONFIG(TAG, "LD2420 Restarted."); | ||||
| } | ||||
|  | ||||
| void LD2420Component::revert_config_action() { | ||||
| @@ -226,18 +223,18 @@ void LD2420Component::revert_config_action() { | ||||
| #ifdef USE_NUMBER | ||||
|   this->init_gate_config_numbers(); | ||||
| #endif | ||||
|   ESP_LOGCONFIG(TAG, "Reverted config number edits."); | ||||
|   ESP_LOGD(TAG, "Reverted config number edits"); | ||||
| } | ||||
|  | ||||
| void LD2420Component::loop() { | ||||
|   // If there is a active send command do not process it here, the send command call will handle it. | ||||
|   if (!get_cmd_active_()) { | ||||
|     if (!available()) | ||||
|   if (!this->get_cmd_active_()) { | ||||
|     if (!this->available()) | ||||
|       return; | ||||
|     static uint8_t buffer[2048]; | ||||
|     static uint8_t rx_data; | ||||
|     while (available()) { | ||||
|       rx_data = read(); | ||||
|     while (this->available()) { | ||||
|       rx_data = this->read(); | ||||
|       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) { | ||||
|   // 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); | ||||
|     // Entering Auto Calibrate we need to clear the privoiuos data collection | ||||
|     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 | ||||
|   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) | ||||
|     return; | ||||
|   this->last_periodic_millis = current_millis; | ||||
|   for (auto &listener : this->listeners_) { | ||||
|     listener->on_distance(get_distance_()); | ||||
|     listener->on_presence(get_presence_()); | ||||
|     listener->on_distance(this->get_distance_()); | ||||
|     listener->on_presence(this->get_presence_()); | ||||
|     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}; | ||||
|   while (true) { | ||||
|     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') { | ||||
|       set_presence_(true); | ||||
|       this->set_presence_(true); | ||||
|     } | ||||
|     if (inbuf[pos] >= '0' && inbuf[pos] <= '9') { | ||||
|       if (index < bufsize - 1) { | ||||
| @@ -411,18 +408,18 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) { | ||||
|   } | ||||
|   outbuf[index] = '\0'; | ||||
|   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 | ||||
|     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) | ||||
|       return; | ||||
|     this->last_normal_periodic_millis = current_millis; | ||||
|     for (auto &listener : this->listeners_) | ||||
|       listener->on_distance(get_distance_()); | ||||
|       listener->on_distance(this->get_distance_()); | ||||
|     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; | ||||
|   uint16_t data_pos = 0; | ||||
|   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; | ||||
|   } 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; | ||||
|   } | ||||
|   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; | ||||
|   switch ((uint16_t) this->cmd_reply_.command) { | ||||
|     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; | ||||
|     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; | ||||
|     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 | ||||
|       data_pos = 0x0A; | ||||
|       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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|       for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE *  // NOLINT | ||||
|                                         ((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; | ||||
|     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; | ||||
|     case (CMD_READ_VERSION): | ||||
|       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; | ||||
|     default: | ||||
|       break; | ||||
| @@ -533,7 +530,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | ||||
|     } | ||||
|  | ||||
|     while (!this->cmd_reply_.ack) { | ||||
|       while (available()) { | ||||
|       while (this->available()) { | ||||
|         this->readline_(read(), ack_buffer, sizeof(ack_buffer)); | ||||
|       } | ||||
|       delay_microseconds_safe(1450); | ||||
| @@ -548,7 +545,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) { | ||||
|     if (this->cmd_reply_.ack) | ||||
|       retry = 0; | ||||
|     if (this->cmd_reply_.error > 0) | ||||
|       handle_cmd_error(error); | ||||
|       this->handle_cmd_error(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.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); | ||||
| } | ||||
|  | ||||
| @@ -576,7 +573,7 @@ void LD2420Component::ld2420_restart() { | ||||
|   cmd_frame.header = CMD_FRAME_HEADER; | ||||
|   cmd_frame.command = CMD_RESTART; | ||||
|   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); | ||||
| } | ||||
|  | ||||
| @@ -588,7 +585,7 @@ void LD2420Component::get_reg_value_(uint16_t reg) { | ||||
|   cmd_frame.data[1] = reg; | ||||
|   cmd_frame.data_length += 2; | ||||
|   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); | ||||
| } | ||||
|  | ||||
| @@ -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)); | ||||
|   cmd_frame.data_length += 2; | ||||
|   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); | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|   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])); | ||||
|   cmd_frame.data_length += 2; | ||||
|   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); | ||||
|   if (error == 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 | ||||
|   cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG); | ||||
|   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); | ||||
|   if (error == 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)); | ||||
|   cmd_frame.data_length += sizeof(unknown_parm); | ||||
|   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) | ||||
|     set_mode_(mode); | ||||
|     this->set_mode_(mode); | ||||
| } | ||||
|  | ||||
| void LD2420Component::get_firmware_version_() { | ||||
| @@ -679,7 +676,7 @@ void LD2420Component::get_firmware_version_() { | ||||
|   cmd_frame.command = CMD_READ_VERSION; | ||||
|   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); | ||||
| } | ||||
|  | ||||
| @@ -712,7 +709,7 @@ void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance, | ||||
|   cmd_frame.data_length += sizeof(timeout); | ||||
|   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); | ||||
| } | ||||
|  | ||||
| @@ -738,7 +735,7 @@ void LD2420Component::set_gate_threshold(uint8_t gate) { | ||||
|          sizeof(this->new_config.still_thresh[gate])); | ||||
|   cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]); | ||||
|   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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -179,7 +179,7 @@ class LD2420Component : public Component, public uart::UARTDevice { | ||||
|   void set_operating_mode(const std::string &state); | ||||
|   void auto_calibrate_sensitivity(); | ||||
|   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 new_config; | ||||
| @@ -222,7 +222,7 @@ class LD2420Component : public Component, public uart::UARTDevice { | ||||
|     volatile bool ack; | ||||
|   }; | ||||
|  | ||||
|   int get_firmware_int_(const char *version_string); | ||||
|   static int get_firmware_int(const char *version_string); | ||||
|   void get_firmware_version_(); | ||||
|   int get_gate_threshold_(uint8_t gate); | ||||
|   void get_reg_value_(uint16_t reg); | ||||
|   | ||||
| @@ -6,7 +6,9 @@ | ||||
| #ifdef USE_SENSOR | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #endif | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| #define highbyte(val) (uint8_t)((val) >> 8) | ||||
| #define lowbyte(val) (uint8_t)((val) &0xff) | ||||
| @@ -96,11 +98,6 @@ static inline std::string get_direction(int16_t speed) { | ||||
|   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) { | ||||
|   return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], | ||||
|                      buffer[14]); | ||||
| @@ -120,7 +117,7 @@ void LD2450Component::setup() { | ||||
| } | ||||
|  | ||||
| void LD2450Component::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:"); | ||||
|   ESP_LOGCONFIG(TAG, "LD2450:"); | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   LOG_BINARY_SENSOR("  ", "TargetBinarySensor", this->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_); | ||||
| #endif | ||||
|   ESP_LOGCONFIG(TAG, | ||||
|                 "  Throttle : %ums\n" | ||||
|                 "  MAC Address : %s\n" | ||||
|                 "  Firmware version : %s", | ||||
|                 "  Throttle: %ums\n" | ||||
|                 "  MAC Address: %s\n" | ||||
|                 "  Firmware version: %s", | ||||
|                 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) { | ||||
|     this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); | ||||
|   } | ||||
|   auto current_millis = millis(); | ||||
|   return current_millis - check_millis >= this->timeout_; | ||||
|   return App.get_loop_component_start_time() - check_millis >= this->timeout_; | ||||
| } | ||||
|  | ||||
| // 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 | ||||
| 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) | ||||
|     ESP_LOGE(TAG, "Periodic data: invalid message length"); | ||||
|     ESP_LOGE(TAG, "Invalid message length"); | ||||
|     return; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|  | ||||
|   auto current_millis = millis(); | ||||
|   if (current_millis - this->last_periodic_millis_ < this->throttle_) { | ||||
|   if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) { | ||||
|     ESP_LOGV(TAG, "Throttling: %d", this->throttle_); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   this->last_periodic_millis_ = current_millis; | ||||
|   this->last_periodic_millis_ = App.get_loop_component_start_time(); | ||||
|  | ||||
|   int16_t 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 | ||||
|   // For presence timeout check | ||||
|   if (target_count > 0) { | ||||
|     this->presence_millis_ = millis(); | ||||
|     this->presence_millis_ = App.get_loop_component_start_time(); | ||||
|   } | ||||
|   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) { | ||||
|     this->still_presence_millis_ = millis(); | ||||
|     this->still_presence_millis_ = App.get_loop_component_start_time(); | ||||
|   } | ||||
| #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) { | ||||
|   ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]); | ||||
|   if (len < 10) { | ||||
|     ESP_LOGE(TAG, "Ack data: invalid length"); | ||||
|     ESP_LOGE(TAG, "Invalid ack length"); | ||||
|     return true; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|   if (buffer[COMMAND_STATUS] != 0x01) { | ||||
|     ESP_LOGE(TAG, "Ack data: invalid status"); | ||||
|     ESP_LOGE(TAG, "Invalid ack status"); | ||||
|     return true; | ||||
|   } | ||||
|   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; | ||||
|   } | ||||
|  | ||||
|   switch (buffer[COMMAND]) { | ||||
|     case lowbyte(CMD_ENABLE_CONF): | ||||
|       ESP_LOGV(TAG, "Got enable conf command"); | ||||
|       ESP_LOGV(TAG, "Enable conf command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_DISABLE_CONF): | ||||
|       ESP_LOGV(TAG, "Got disable conf command"); | ||||
|       ESP_LOGV(TAG, "Disable conf command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_BAUD_RATE): | ||||
|       ESP_LOGV(TAG, "Got baud rate change command"); | ||||
|       ESP_LOGV(TAG, "Baud rate change command"); | ||||
| #ifdef USE_SELECT | ||||
|       if (this->baud_rate_select_ != nullptr) { | ||||
|         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) { | ||||
|         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()); | ||||
| #ifdef USE_TEXT_SENSOR | ||||
|       if (this->mac_text_sensor_ != nullptr) { | ||||
| @@ -622,15 +617,15 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
|       if (this->bluetooth_switch_ != nullptr) { | ||||
|         this->bluetooth_switch_->publish_state(this->mac_ != NO_MAC); | ||||
|         this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC); | ||||
|       } | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_BLUETOOTH): | ||||
|       ESP_LOGV(TAG, "Got Bluetooth command"); | ||||
|       ESP_LOGV(TAG, "Bluetooth command"); | ||||
|       break; | ||||
|     case lowbyte(CMD_SINGLE_TARGET_MODE): | ||||
|       ESP_LOGV(TAG, "Got single target conf command"); | ||||
|       ESP_LOGV(TAG, "Single target conf command"); | ||||
| #ifdef USE_SWITCH | ||||
|       if (this->multi_target_switch_ != nullptr) { | ||||
|         this->multi_target_switch_->publish_state(false); | ||||
| @@ -638,7 +633,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||
| #endif | ||||
|       break; | ||||
|     case lowbyte(CMD_MULTI_TARGET_MODE): | ||||
|       ESP_LOGV(TAG, "Got multi target conf command"); | ||||
|       ESP_LOGV(TAG, "Multi target conf command"); | ||||
| #ifdef USE_SWITCH | ||||
|       if (this->multi_target_switch_ != nullptr) { | ||||
|         this->multi_target_switch_->publish_state(true); | ||||
| @@ -646,7 +641,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||
| #endif | ||||
|       break; | ||||
|     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 | ||||
|       if (this->multi_target_switch_ != nullptr) { | ||||
|         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 | ||||
|       break; | ||||
|     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->publish_zone_type(); | ||||
| #ifdef USE_SELECT | ||||
| @@ -674,7 +669,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { | ||||
|       this->process_zone_(buffer); | ||||
|       break; | ||||
|     case lowbyte(CMD_SET_ZONE): | ||||
|       ESP_LOGV(TAG, "Got set zone conf command"); | ||||
|       ESP_LOGV(TAG, "Set zone conf command"); | ||||
|       this->query_zone_info(); | ||||
|       break; | ||||
|     default: | ||||
|   | ||||
| @@ -38,8 +38,8 @@ from esphome.const import ( | ||||
|     CONF_WHITE, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| from .automation import LIGHT_STATE_SCHEMA | ||||
| from .effects import ( | ||||
| @@ -110,6 +110,8 @@ LIGHT_SCHEMA = ( | ||||
|     ) | ||||
| ) | ||||
|  | ||||
| LIGHT_SCHEMA.add_extra(entity_duplicate_validator("light")) | ||||
|  | ||||
| BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend( | ||||
|     { | ||||
|         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): | ||||
|     await setup_entity(light_var, config) | ||||
|     await setup_entity(light_var, config, "light") | ||||
|  | ||||
|     cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) | ||||
|  | ||||
|   | ||||
| @@ -14,8 +14,8 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -67,6 +67,9 @@ _LOCK_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _LOCK_SCHEMA.add_extra(entity_duplicate_validator("lock")) | ||||
|  | ||||
|  | ||||
| def lock_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
|     *, | ||||
| @@ -94,7 +97,7 @@ LOCK_SCHEMA.add_extra(cv.deprecated_schema_constant("lock")) | ||||
|  | ||||
|  | ||||
| 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, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|   | ||||
| @@ -184,7 +184,9 @@ CONFIG_SCHEMA = cv.All( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(Logger), | ||||
|             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.SplitDefault( | ||||
|                 CONF_TASK_LOG_BUFFER_SIZE, | ||||
|   | ||||
| @@ -24,7 +24,7 @@ static const char *const TAG = "logger"; | ||||
| //    - Messages are serialized through main loop for proper console output | ||||
| //    - Fallback to emergency console logging only if ring buffer is full | ||||
| //  - 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)) | ||||
|     return; | ||||
|  | ||||
| @@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char * | ||||
|   bool message_sent = false; | ||||
| #ifdef USE_ESPHOME_TASK_LOG_BUFFER | ||||
|   // 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, | ||||
|                                                              static_cast<uint16_t>(line), current_task, format, args); | ||||
|   message_sent = | ||||
|       this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args); | ||||
| #endif  // USE_ESPHOME_TASK_LOG_BUFFER | ||||
|  | ||||
|   // 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) | ||||
|     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 | ||||
|     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, | ||||
|                                                 MAX_CONSOLE_LOG_MSG_SIZE); | ||||
|     this->write_msg_(console_buffer); | ||||
| @@ -69,7 +69,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char * | ||||
| } | ||||
| #else | ||||
| // 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_) | ||||
|     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 | ||||
| // Implementation for ESP8266 with flash string support. | ||||
| // 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 | ||||
|   if (level > this->level_for(tag) || global_recursion_guard_) | ||||
|     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 | ||||
|  | ||||
| inline int Logger::level_for(const char *tag) { | ||||
| inline uint8_t Logger::level_for(const char *tag) { | ||||
|   auto it = this->log_levels_.find(tag); | ||||
|   if (it != this->log_levels_.end()) | ||||
|     return it->second; | ||||
| @@ -195,13 +195,13 @@ void Logger::loop() { | ||||
| #endif | ||||
|  | ||||
| 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) | ||||
| UARTSelection Logger::get_uart() const { return this->uart_; } | ||||
| #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)); | ||||
| } | ||||
| 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) { | ||||
|     level = ESPHOME_LOG_LEVEL; | ||||
|     ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); | ||||
|   | ||||
| @@ -61,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = { | ||||
|  * | ||||
|  * Advanced configuration (pin selection, etc) is not supported. | ||||
|  */ | ||||
| enum UARTSelection { | ||||
| enum UARTSelection : uint8_t { | ||||
| #ifdef USE_LIBRETINY | ||||
|   UART_SELECTION_DEFAULT = 0, | ||||
|   UART_SELECTION_UART0, | ||||
| @@ -129,10 +129,10 @@ class Logger : public Component { | ||||
| #endif | ||||
|  | ||||
|   /// 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. | ||||
|   void set_log_level(const std::string &tag, int log_level); | ||||
|   int get_log_level() { return this->current_level_; } | ||||
|   void set_log_level(const std::string &tag, uint8_t log_level); | ||||
|   uint8_t get_log_level() { return this->current_level_; } | ||||
|  | ||||
|   // ========== INTERNAL METHODS ========== | ||||
|   // (In most use cases you won't need these) | ||||
| @@ -140,19 +140,20 @@ class Logger : public Component { | ||||
|   void pre_setup(); | ||||
|   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 | ||||
|   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 | ||||
|   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; | ||||
|  | ||||
|   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 | ||||
|   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 | ||||
|  | ||||
|  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 | ||||
|   // 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, | ||||
|                                                         va_list args, char *buffer, int *buffer_at, int buffer_size) { | ||||
|   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, uint16_t *buffer_at, | ||||
|                                                         uint16_t buffer_size) { | ||||
| #if defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||
|     this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size); | ||||
| #else | ||||
| @@ -180,7 +182,7 @@ class Logger : public Component { | ||||
|   } | ||||
|  | ||||
|   // 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) { | ||||
|     // Format to tx_buffer and prepare for output | ||||
|     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 | ||||
|   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 | ||||
|     const int available = buffer_size - *buffer_at; | ||||
|     if (available <= 0) | ||||
|     if (*buffer_at >= buffer_size) | ||||
|       return; | ||||
|     const uint16_t available = buffer_size - *buffer_at; | ||||
|  | ||||
|     // Determine copy length (minimum of remaining capacity and string length) | ||||
|     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 | ||||
|   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_start(arg, format); | ||||
|     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_(); | ||||
| #endif | ||||
|  | ||||
|   // Group 4-byte aligned members first | ||||
|   uint32_t baud_rate_; | ||||
|   char *tx_buffer_{nullptr}; | ||||
|   int tx_buffer_at_{0}; | ||||
|   int tx_buffer_size_{0}; | ||||
| #ifdef USE_ARDUINO | ||||
|   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) | ||||
|   UARTSelection uart_{UART_SELECTION_UART0}; | ||||
| #endif | ||||
| #ifdef USE_LIBRETINY | ||||
|   UARTSelection uart_{UART_SELECTION_DEFAULT}; | ||||
| #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 | ||||
|   // 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}; | ||||
|   pthread_key_t log_recursion_key_; | ||||
| #else | ||||
|   bool global_recursion_guard_{false};  // Simple global recursion guard for single-task platforms | ||||
| #endif | ||||
|   CallbackManager<void(int)> level_callback_{}; | ||||
|  | ||||
| #if defined(USE_ESP32) || defined(USE_LIBRETINY) | ||||
|   void *main_task_ = nullptr;  // Only used for thread name identification | ||||
|   const char *HOT get_thread_name_() { | ||||
|     TaskHandle_t current_task = xTaskGetCurrentTaskHandle(); | ||||
|     if (current_task == main_task_) { | ||||
| @@ -297,11 +309,10 @@ class Logger : public Component { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer, | ||||
|                                           int *buffer_at, int buffer_size) { | ||||
|   inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name, | ||||
|                                           char *buffer, uint16_t *buffer_at, uint16_t buffer_size) { | ||||
|     // Format header | ||||
|     if (level < 0) | ||||
|       level = 0; | ||||
|     // uint8_t level is already bounded 0-255, just ensure it's <= 7 | ||||
|     if (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); | ||||
|   } | ||||
|  | ||||
|   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) { | ||||
|     // Get remaining capacity in the buffer | ||||
|     const int remaining = buffer_size - *buffer_at; | ||||
|     if (remaining <= 0) | ||||
|     if (*buffer_at >= buffer_size) | ||||
|       return; | ||||
|     const uint16_t remaining = buffer_size - *buffer_at; | ||||
|  | ||||
|     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) | ||||
|     int formatted_len = (ret >= remaining) ? remaining : ret; | ||||
|     uint16_t formatted_len = (ret >= remaining) ? remaining : ret; | ||||
|     *buffer_at += formatted_len; | ||||
|  | ||||
|     // 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) { | ||||
|     static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR); | ||||
|   inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) { | ||||
|     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); | ||||
|   } | ||||
| }; | ||||
| 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: | ||||
|   explicit LoggerMessageTrigger(Logger *parent, int level) { | ||||
|   explicit LoggerMessageTrigger(Logger *parent, uint8_t 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_) { | ||||
|         this->trigger(level, tag, message); | ||||
|       } | ||||
| @@ -362,7 +373,7 @@ class LoggerMessageTrigger : public Trigger<int, const char *, const char *> { | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   int level_; | ||||
|   uint8_t level_; | ||||
| }; | ||||
|  | ||||
| }  // namespace logger | ||||
|   | ||||
| @@ -454,9 +454,13 @@ def container_validator(schema, widget_type: WidgetType): | ||||
|     """ | ||||
|  | ||||
|     def validator(value): | ||||
|         result = 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 | ||||
|         if value and (layout := value.get(df.CONF_LAYOUT)): | ||||
|             if not isinstance(layout, dict): | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_ID | ||||
| from esphome.core import ID | ||||
| from esphome.cpp_generator import MockObj | ||||
|  | ||||
| from .defines import ( | ||||
|     CONF_STYLE_DEFINITIONS, | ||||
| @@ -13,12 +12,13 @@ from .defines import ( | ||||
|     literal, | ||||
| ) | ||||
| 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 .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 ( | ||||
|     Widget, | ||||
|     add_widgets, | ||||
|     collect_parts, | ||||
|     set_obj_properties, | ||||
|     theme_widget_map, | ||||
|     wait_for_widgets, | ||||
| @@ -37,12 +37,18 @@ async def style_set(svar, style): | ||||
|             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): | ||||
|     """Convert styles to C__ code.""" | ||||
|     for style in config.get(CONF_STYLE_DEFINITIONS, ()): | ||||
|         svar = cg.new_Pvariable(style[CONF_ID]) | ||||
|         lv.style_init(svar) | ||||
|         await style_set(svar, style) | ||||
|         await create_style(style, style[CONF_ID].id) | ||||
|  | ||||
|  | ||||
| @automation.register_action( | ||||
| @@ -68,16 +74,18 @@ async def theme_to_code(config): | ||||
|     if theme := config.get(CONF_THEME): | ||||
|         add_lv_use(CONF_THEME) | ||||
|         for w_name, style in theme.items(): | ||||
|             if not isinstance(style, dict): | ||||
|                 continue | ||||
|  | ||||
|             lname = "lv_theme_apply_" + w_name | ||||
|             apply = lv_variable(lv_lambda_t, lname) | ||||
|             theme_widget_map[w_name] = apply | ||||
|             ow = Widget.create("obj", MockObj(ID("obj")), obj_spec) | ||||
|             async with LambdaContext([(lv_obj_t_ptr, "obj")], where=w_name) as context: | ||||
|                 await set_obj_properties(ow, style) | ||||
|             lv_assign(apply, await context.get_lambda()) | ||||
|             # Work around Python 3.10 bug with nested async comprehensions | ||||
|             # With Python 3.11 this could be simplified | ||||
|             styles = {} | ||||
|             for part, states in collect_parts(style).items(): | ||||
|                 styles[part] = { | ||||
|                     state: await create_style( | ||||
|                         props, | ||||
|                         "_lv_theme_style_" + w_name + "_" + part + "_" + state, | ||||
|                     ) | ||||
|                     for state, props in states.items() | ||||
|                 } | ||||
|             theme_widget_map[w_name] = styles | ||||
|  | ||||
|  | ||||
| async def add_top_layer(lv_component, config): | ||||
|   | ||||
| @@ -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.core import ID, TimePeriod | ||||
| from esphome.coroutine import FakeAwaitable | ||||
| from esphome.cpp_generator import CallExpression, MockObj | ||||
| from esphome.cpp_generator import MockObj | ||||
|  | ||||
| from ..defines import ( | ||||
|     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) | ||||
|     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 add_widgets(w, w_cnfig) | ||||
|     await spec.to_code(w, w_cnfig) | ||||
|   | ||||
| @@ -1,8 +1,15 @@ | ||||
| import esphome.config_validation as cv | ||||
| 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 ..lv_validation import animated, get_start_value, lv_float | ||||
| from ..defines import ( | ||||
|     BAR_MODES, | ||||
|     CONF_ANIMATED, | ||||
|     CONF_INDICATOR, | ||||
|     CONF_MAIN, | ||||
|     CONF_START_VALUE, | ||||
|     literal, | ||||
| ) | ||||
| from ..lv_validation import animated, lv_int | ||||
| from ..lvcode import lv | ||||
| from ..types import LvNumber, NumberType | ||||
| from . import Widget | ||||
| @@ -10,22 +17,30 @@ from . import Widget | ||||
| # Note this file cannot be called "bar.py" because that name is disallowed. | ||||
|  | ||||
| CONF_BAR = "bar" | ||||
| BAR_MODIFY_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Optional(CONF_VALUE): lv_float, | ||||
|         cv.Optional(CONF_ANIMATED, default=True): animated, | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| def validate_bar(config): | ||||
|     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( | ||||
|     { | ||||
|         cv.Optional(CONF_VALUE): lv_float, | ||||
|         cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, | ||||
|         cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, | ||||
|         cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of, | ||||
|         cv.Optional(CONF_VALUE): lv_int, | ||||
|         cv.Optional(CONF_START_VALUE): lv_int, | ||||
|         cv.Optional(CONF_MIN_VALUE): lv_int, | ||||
|         cv.Optional(CONF_MAX_VALUE): lv_int, | ||||
|         cv.Optional(CONF_MODE): BAR_MODES.one_of, | ||||
|         cv.Optional(CONF_ANIMATED, default=True): animated, | ||||
|     } | ||||
| ) | ||||
| ).add_extra(validate_bar) | ||||
|  | ||||
|  | ||||
| class BarType(NumberType): | ||||
| @@ -35,17 +50,23 @@ class BarType(NumberType): | ||||
|             LvNumber("lv_bar_t"), | ||||
|             parts=(CONF_MAIN, CONF_INDICATOR), | ||||
|             schema=BAR_SCHEMA, | ||||
|             modify_schema=BAR_MODIFY_SCHEMA, | ||||
|         ) | ||||
|  | ||||
|     async def to_code(self, w: Widget, config): | ||||
|         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: | ||||
|             lv.bar_set_range(var, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE]) | ||||
|             lv.bar_set_mode(var, literal(config[CONF_MODE])) | ||||
|         value = await get_start_value(config) | ||||
|         if value is not None: | ||||
|             lv.bar_set_value(var, value, literal(config[CONF_ANIMATED])) | ||||
|             lv.bar_set_range( | ||||
|                 var, | ||||
|                 await lv_int.process(config[CONF_MIN_VALUE]), | ||||
|                 await lv_int.process(config[CONF_MAX_VALUE]), | ||||
|             ) | ||||
|         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 | ||||
|     def animated(self): | ||||
|   | ||||
| @@ -3,7 +3,7 @@ import esphome.config_validation as cv | ||||
| from esphome.const import CONF_SIZE, CONF_TEXT | ||||
| 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 ..lvcode import LocalVariable, lv, lv_expr | ||||
| from ..schemas import TEXT_SCHEMA | ||||
| @@ -34,7 +34,7 @@ class QrCodeType(WidgetType): | ||||
|         ) | ||||
|  | ||||
|     def get_uses(self): | ||||
|         return ("canvas", "img") | ||||
|         return ("canvas", "img", "label") | ||||
|  | ||||
|     def obj_creator(self, parent: MockObjClass, config: dict): | ||||
|         dark_color = color_retmapper(config[CONF_DARK_COLOR]) | ||||
| @@ -45,10 +45,8 @@ class QrCodeType(WidgetType): | ||||
|     async def to_code(self, w: Widget, config): | ||||
|         if (value := config.get(CONF_TEXT)) is not None: | ||||
|             value = await lv_text.process(value) | ||||
|             with LocalVariable( | ||||
|                 "qr_text", cg.const_char_ptr, value, modifier="" | ||||
|             ) as str_obj: | ||||
|                 lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})")) | ||||
|             with LocalVariable("qr_text", cg.std_string, value, modifier="") as str_obj: | ||||
|                 lv.qrcode_update(w.obj, str_obj.c_str(), str_obj.size()) | ||||
|  | ||||
|  | ||||
| qr_code_spec = QrCodeType() | ||||
|   | ||||
| @@ -8,7 +8,7 @@ namespace m5stack_8angle { | ||||
| static const char *const TAG = "m5stack_8angle.light"; | ||||
|  | ||||
| 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); | ||||
|   if (this->buf_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); | ||||
|   | ||||
| @@ -11,9 +11,9 @@ from esphome.const import ( | ||||
|     CONF_VOLUME, | ||||
| ) | ||||
| 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.cpp_generator import MockObjClass | ||||
| from esphome.cpp_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@jesserockz"] | ||||
|  | ||||
| @@ -81,7 +81,7 @@ IsAnnouncingCondition = media_player_ns.class_( | ||||
|  | ||||
|  | ||||
| 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, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|         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( | ||||
|     class_: MockObjClass, | ||||
| @@ -166,7 +168,6 @@ def media_player_schema( | ||||
| MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer) | ||||
| MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player")) | ||||
|  | ||||
|  | ||||
| MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id( | ||||
|     cv.Schema( | ||||
|         { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ void VADModel::log_model_config() { | ||||
| } | ||||
|  | ||||
| bool StreamingModel::load_model_() { | ||||
|   RAMAllocator<uint8_t> arena_allocator(RAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> arena_allocator; | ||||
|  | ||||
|   if (this->tensor_arena_ == nullptr) { | ||||
|     this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_); | ||||
| @@ -96,7 +96,7 @@ bool StreamingModel::load_model_() { | ||||
| void StreamingModel::unload_model() { | ||||
|   this->interpreter_.reset(); | ||||
|  | ||||
|   RAMAllocator<uint8_t> arena_allocator(RAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> arena_allocator; | ||||
|  | ||||
|   if (this->tensor_arena_ != nullptr) { | ||||
|     arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_); | ||||
|   | ||||
| @@ -472,3 +472,4 @@ async def to_code(config): | ||||
|         cg.add(var.set_writer(lambda_)) | ||||
|     await display.register_display(var, config) | ||||
|     await spi.register_spi_device(var, config) | ||||
|     cg.add(var.set_write_only(True)) | ||||
|   | ||||
| @@ -64,6 +64,14 @@ class ModbusDevice { | ||||
|     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_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 | ||||
|   bool waiting_for_response() { return parent_->waiting_for_response != 0; } | ||||
|  | ||||
|   | ||||
| @@ -112,6 +112,22 @@ TYPE_REGISTER_MAP = { | ||||
|     "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", 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])) | ||||
|     if CONF_SERVER_REGISTERS in config: | ||||
|         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( | ||||
|                 var.add_server_register( | ||||
|                     cg.new_Pvariable( | ||||
|                         server_register[CONF_ID], | ||||
|                         server_register[CONF_ADDRESS], | ||||
|                         server_register[CONF_VALUE_TYPE], | ||||
|                         TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]], | ||||
|                         await cg.process_lambda( | ||||
|                             server_register[CONF_READ_LAMBDA], | ||||
|                             [], | ||||
|                             return_type=cg.float_, | ||||
|                         ), | ||||
|                     ) | ||||
|                 server_register_var.set_read_lambda( | ||||
|                     cg.TemplateArguments(cpp_type), | ||||
|                     await cg.process_lambda( | ||||
|                         server_register[CONF_READ_LAMBDA], | ||||
|                         [(cg.uint16, "address")], | ||||
|                         return_type=cpp_type, | ||||
|                     ), | ||||
|                 ) | ||||
|             ) | ||||
|             cg.add(var.add_server_register(server_register_var)) | ||||
|     await register_modbus_device(var, config) | ||||
|     for conf in config.get(CONF_ON_COMMAND_SENT, []): | ||||
|         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||
|   | ||||
| @@ -117,12 +117,17 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t | ||||
|     bool found = false; | ||||
|     for (auto *server_register : this->server_registers_) { | ||||
|       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.", | ||||
|                  server_register->address, static_cast<uint8_t>(server_register->value_type), | ||||
|                  server_register->register_count, value); | ||||
|         std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type); | ||||
|         std::vector<uint16_t> payload; | ||||
|         payload.reserve(server_register->register_count * 2); | ||||
|         number_to_payload(payload, value, server_register->value_type); | ||||
|         sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend()); | ||||
|         current_address += server_register->register_count; | ||||
|         found = true; | ||||
| @@ -132,11 +137,7 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t | ||||
|  | ||||
|     if (!found) { | ||||
|       ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address); | ||||
|       std::vector<uint8_t> error_response; | ||||
|       error_response.push_back(this->address_); | ||||
|       error_response.push_back(0x81); | ||||
|       error_response.push_back(0x02); | ||||
|       this->send_raw(error_response); | ||||
|       send_error(function_code, 0x02); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -63,6 +63,10 @@ enum class SensorValueType : uint8_t { | ||||
|   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) { | ||||
|   switch (reg_type) { | ||||
|     case ModbusRegisterType::COIL: | ||||
| @@ -253,18 +257,53 @@ class SensorItem { | ||||
| }; | ||||
|  | ||||
| class ServerRegister { | ||||
|   using ReadLambda = std::function<int64_t()>; | ||||
|  | ||||
|  public: | ||||
|   ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count, | ||||
|                  std::function<float()> read_lambda) { | ||||
|   ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count) { | ||||
|     this->address = address; | ||||
|     this->value_type = value_type; | ||||
|     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}; | ||||
|   SensorValueType value_type{SensorValueType::RAW}; | ||||
|   uint8_t register_count{0}; | ||||
|   std::function<float()> read_lambda; | ||||
|   ReadLambda read_lambda; | ||||
| }; | ||||
|  | ||||
| // 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; | ||||
|   /// called when a modbus error response was received | ||||
|   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; | ||||
|   /// 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); | ||||
| @@ -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); | ||||
|  | ||||
|   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)); | ||||
|   } else { | ||||
|     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) { | ||||
|   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); | ||||
|   } else { | ||||
|     val = llroundf(value); | ||||
|   | ||||
| @@ -68,6 +68,7 @@ def AUTO_LOAD(): | ||||
|  | ||||
| CONF_DISCOVER_IP = "discover_ip" | ||||
| CONF_IDF_SEND_ASYNC = "idf_send_async" | ||||
| CONF_WAIT_FOR_CONNECTION = "wait_for_connection" | ||||
|  | ||||
|  | ||||
| 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_WAIT_FOR_CONNECTION, default=False): cv.boolean, | ||||
|         } | ||||
|     ), | ||||
|     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_wait_for_connection(config[CONF_WAIT_FOR_CONNECTION])) | ||||
|  | ||||
|  | ||||
| MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema( | ||||
|     { | ||||
|   | ||||
| @@ -17,7 +17,8 @@ enum class MQTTClientDisconnectReason : int8_t { | ||||
|   MQTT_MALFORMED_CREDENTIALS = 4, | ||||
|   MQTT_NOT_AUTHORIZED = 5, | ||||
|   ESP8266_NOT_ENOUGH_SPACE = 6, | ||||
|   TLS_BAD_FINGERPRINT = 7 | ||||
|   TLS_BAD_FINGERPRINT = 7, | ||||
|   DNS_RESOLVE_ERROR = 8 | ||||
| }; | ||||
|  | ||||
| /// internal struct for MQTT messages. | ||||
|   | ||||
| @@ -176,7 +176,8 @@ void MQTTClientComponent::dump_config() { | ||||
|   } | ||||
| } | ||||
| 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_() { | ||||
| @@ -228,6 +229,8 @@ void MQTTClientComponent::check_dnslookup_() { | ||||
|   if (this->dns_resolve_error_) { | ||||
|     ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); | ||||
|     this->state_ = MQTT_CLIENT_DISCONNECTED; | ||||
|     this->disconnect_reason_ = MQTTClientDisconnectReason::DNS_RESOLVE_ERROR; | ||||
|     this->on_disconnect_.call(MQTTClientDisconnectReason::DNS_RESOLVE_ERROR); | ||||
|     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) { | ||||
|   auto callback_copy = 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 | ||||
|   | ||||
| @@ -4,11 +4,12 @@ | ||||
|  | ||||
| #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/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) | ||||
| #include "mqtt_backend_esp32.h" | ||||
| #elif defined(USE_ESP8266) | ||||
| @@ -267,6 +268,8 @@ class MQTTClientComponent : public Component { | ||||
|   void set_publish_nan_as_none(bool publish_nan_as_none); | ||||
|   bool is_publish_nan_as_none() const; | ||||
|  | ||||
|   void set_wait_for_connection(bool wait_for_connection) { this->wait_for_connection_ = wait_for_connection; } | ||||
|  | ||||
|  protected: | ||||
|   void send_device_info_(); | ||||
|  | ||||
| @@ -332,8 +335,10 @@ class MQTTClientComponent : public Component { | ||||
|   uint32_t connect_begin_; | ||||
|   uint32_t last_connected_{0}; | ||||
|   optional<MQTTClientDisconnectReason> disconnect_reason_{}; | ||||
|   CallbackManager<MQTTBackend::on_disconnect_callback_t> on_disconnect_; | ||||
|  | ||||
|   bool publish_nan_as_none_{false}; | ||||
|   bool wait_for_connection_{false}; | ||||
| }; | ||||
|  | ||||
| extern MQTTClientComponent *global_mqtt_client;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| #include "nextion.h" | ||||
| #include "esphome/core/util.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include <cinttypes> | ||||
| #include "esphome/core/application.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/util.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace nextion { | ||||
| @@ -33,6 +33,7 @@ bool Nextion::send_command_(const std::string &command) { | ||||
|  | ||||
| #ifdef USE_NEXTION_COMMAND_SPACING | ||||
|   if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) { | ||||
|     ESP_LOGN(TAG, "Command spacing: delaying command '%s'", command.c_str()); | ||||
|     return false; | ||||
|   } | ||||
| #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}; | ||||
|   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; | ||||
| } | ||||
|  | ||||
| @@ -74,13 +71,13 @@ bool Nextion::check_connect_() { | ||||
|     } | ||||
|     this->send_command_("connect"); | ||||
|  | ||||
|     this->comok_sent_ = millis(); | ||||
|     this->comok_sent_ = App.get_loop_component_start_time(); | ||||
|     this->ignore_is_setup_ = 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; | ||||
|  | ||||
|   std::string response; | ||||
| @@ -321,15 +318,38 @@ void Nextion::loop() { | ||||
|  | ||||
|   if (!this->nextion_reports_is_setup_) { | ||||
|     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"); | ||||
|       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) { | ||||
|   if (this->nextion_queue_.empty()) { | ||||
|     if (report_empty) { | ||||
| @@ -377,12 +397,6 @@ void Nextion::process_nextion_commands_() { | ||||
|   size_t commands_processed = 0; | ||||
| #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; | ||||
|   std::string to_process; | ||||
|  | ||||
| @@ -418,7 +432,7 @@ void Nextion::process_nextion_commands_() { | ||||
|       case 0x01:  // instruction sent by user was successful | ||||
|  | ||||
|         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_(); | ||||
|         if (!this->is_setup_) { | ||||
| @@ -430,6 +444,7 @@ void Nextion::process_nextion_commands_() { | ||||
|         } | ||||
| #ifdef USE_NEXTION_COMMAND_SPACING | ||||
|         this->command_pacer_.mark_sent();  // Here is where we should mark the command as sent | ||||
|         ESP_LOGN(TAG, "Command spacing: marked command sent"); | ||||
| #endif | ||||
|         break; | ||||
|       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); | ||||
|   } | ||||
|  | ||||
|   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) { | ||||
|     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; | ||||
|   uint8_t c = 0; | ||||
|   uint8_t nr_of_ff_bytes = 0; | ||||
|   uint64_t start; | ||||
|   bool exit_flag = false; | ||||
|   bool ff_flag = false; | ||||
|  | ||||
|   start = millis(); | ||||
|   const uint32_t start = millis(); | ||||
|  | ||||
|   while ((timeout == 0 && this->available()) || millis() - start <= timeout) { | ||||
|     if (!this->available()) { | ||||
| @@ -1011,7 +1025,7 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); | ||||
|   RAMAllocator<nextion::NextionQueue> allocator; | ||||
|   nextion::NextionQueue *nextion_queue = allocator.allocate(1); | ||||
|   if (nextion_queue == nullptr) { | ||||
|     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)) { | ||||
|     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, | ||||
|                                                                ...) { | ||||
|   if ((!this->is_setup() && !this->ignore_is_setup_)) | ||||
| @@ -1167,7 +1214,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) { | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); | ||||
|   RAMAllocator<nextion::NextionQueue> allocator; | ||||
|   nextion::NextionQueue *nextion_queue = allocator.allocate(1); | ||||
|   if (nextion_queue == nullptr) { | ||||
|     ESP_LOGW(TAG, "Queue alloc failed"); | ||||
| @@ -1176,7 +1223,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) { | ||||
|   new (nextion_queue) nextion::NextionQueue(); | ||||
|  | ||||
|   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()); | ||||
|  | ||||
| @@ -1199,7 +1246,7 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { | ||||
|   if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) | ||||
|     return; | ||||
|  | ||||
|   ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); | ||||
|   RAMAllocator<nextion::NextionQueue> allocator; | ||||
|   nextion::NextionQueue *nextion_queue = allocator.allocate(1); | ||||
|   if (nextion_queue == nullptr) { | ||||
|     ESP_LOGW(TAG, "Queue alloc failed"); | ||||
| @@ -1208,7 +1255,7 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { | ||||
|   new (nextion_queue) nextion::NextionQueue(); | ||||
|  | ||||
|   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); | ||||
|   if (this->waveform_queue_.size() == 1) | ||||
|   | ||||
| @@ -1309,9 +1309,23 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe | ||||
| #ifdef USE_NEXTION_MAX_QUEUE_SIZE | ||||
|   size_t max_queue_size_{0}; | ||||
| #endif  // USE_NEXTION_MAX_QUEUE_SIZE | ||||
|  | ||||
| #ifdef USE_NEXTION_COMMAND_SPACING | ||||
|   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 | ||||
|  | ||||
|   std::deque<NextionQueue *> nextion_queue_; | ||||
|   std::deque<NextionQueue *> waveform_queue_; | ||||
|   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))); | ||||
|   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, ...) | ||||
|       __attribute__((format(printf, 3, 4))); | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,9 @@ class NextionQueue { | ||||
|   virtual ~NextionQueue() = default; | ||||
|   NextionComponentBase *component; | ||||
|   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 { | ||||
|   | ||||
							
								
								
									
										36
									
								
								esphome/components/nextion/nextion_upload.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/nextion/nextion_upload.cpp
									
									
									
									
									
										Normal 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 | ||||
| @@ -3,12 +3,12 @@ | ||||
| #ifdef USE_NEXTION_TFT_UPLOAD | ||||
| #ifdef USE_ARDUINO | ||||
|  | ||||
| #include <cinttypes> | ||||
| #include "esphome/components/network/util.h" | ||||
| #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 "esphome/core/util.h" | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| #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 | ||||
|   ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> allocator; | ||||
|   uint8_t *buffer = allocator.allocate(4096); | ||||
|   if (!buffer) { | ||||
|     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); | ||||
|     uint16_t read_len = 0; | ||||
|     int partial_read_len = 0; | ||||
|     const uint32_t start_time = millis(); | ||||
|     while (read_len < buffer_size && millis() - start_time < 5000) { | ||||
|     const uint32_t start_time = App.get_loop_component_start_time(); | ||||
|     while (read_len < buffer_size && App.get_loop_component_start_time() - start_time < 5000) { | ||||
|       if (http_client.getStreamPtr()->available() > 0) { | ||||
|         partial_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); | ||||
| } | ||||
|  | ||||
| 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 | ||||
| WiFiClient *Nextion::get_wifi_client_() { | ||||
|   if (this->tft_url_.compare(0, 6, "https:") == 0) { | ||||
|   | ||||
| @@ -3,14 +3,14 @@ | ||||
| #ifdef USE_NEXTION_TFT_UPLOAD | ||||
| #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_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 nextion { | ||||
| @@ -51,7 +51,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r | ||||
|   } | ||||
|  | ||||
|   // Allocate the buffer dynamically | ||||
|   ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); | ||||
|   RAMAllocator<uint8_t> allocator; | ||||
|   uint8_t *buffer = allocator.allocate(4096); | ||||
|   if (!buffer) { | ||||
|     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); | ||||
| } | ||||
|  | ||||
| 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 esphome | ||||
|  | ||||
|   | ||||
| @@ -76,8 +76,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_WIND_SPEED, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| DEVICE_CLASSES = [ | ||||
| @@ -207,6 +207,9 @@ _NUMBER_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _NUMBER_SCHEMA.add_extra(entity_duplicate_validator("number")) | ||||
|  | ||||
|  | ||||
| def number_schema( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -237,7 +240,7 @@ NUMBER_SCHEMA.add_extra(cv.deprecated_schema_constant("number")) | ||||
| async def setup_number_core_( | ||||
|     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_max_value(max_value)) | ||||
|   | ||||
| @@ -34,6 +34,7 @@ MULTI_CONF = True | ||||
|  | ||||
| CONF_ON_DOWNLOAD_FINISHED = "on_download_finished" | ||||
| CONF_PLACEHOLDER = "placeholder" | ||||
| CONF_UPDATE = "update" | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -167,6 +168,7 @@ SET_URL_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(): cv.use_id(OnlineImage), | ||||
|         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: | ||||
|         template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) | ||||
|         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 | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -201,9 +201,12 @@ template<typename... Ts> class OnlineImageSetUrlAction : public Action<Ts...> { | ||||
|  public: | ||||
|   OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {} | ||||
|   TEMPLATABLE_VALUE(std::string, url) | ||||
|   TEMPLATABLE_VALUE(bool, update) | ||||
|   void play(Ts... x) override { | ||||
|     this->parent_->set_url(this->url_.value(x...)); | ||||
|     this->parent_->update(); | ||||
|     if (this->update_.value(x...)) { | ||||
|       this->parent_->update(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   | ||||
| @@ -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_CHANNEL", config[CONF_CHANNEL]) | ||||
|     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): | ||||
| @@ -54,14 +54,14 @@ def set_sdkconfig_options(config): | ||||
|  | ||||
|     if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None: | ||||
|         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: | ||||
|         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: | ||||
|         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 config[CONF_FORCE_DATASET]: | ||||
| @@ -98,7 +98,7 @@ _CONNECTION_SCHEMA = cv.Schema( | ||||
|         cv.Optional(CONF_EXT_PAN_ID): cv.hex_int, | ||||
|         cv.Optional(CONF_NETWORK_NAME): cv.string_strict, | ||||
|         cv.Optional(CONF_PSKC): cv.hex_int, | ||||
|         cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int, | ||||
|         cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.ipv6network, | ||||
|     } | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   // component | ||||
|   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_) { | ||||
|     otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance); | ||||
|     if (!entry) { | ||||
| @@ -185,11 +185,11 @@ void OpenThreadSrpComponent::setup() { | ||||
|     if (error != OT_ERROR_NONE) { | ||||
|       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); | ||||
|   ESP_LOGW(TAG, "Finished SRP setup"); | ||||
|   ESP_LOGD(TAG, "Finished SRP setup"); | ||||
| } | ||||
|  | ||||
| void *OpenThreadSrpComponent::pool_alloc_(size_t size) { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| # Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9 | ||||
| import binascii | ||||
| import ipaddress | ||||
|  | ||||
| from esphome.const import CONF_CHANNEL | ||||
|  | ||||
| @@ -37,6 +38,12 @@ def parse_tlv(tlv) -> dict: | ||||
|         if tag in TLV_TYPES: | ||||
|             if tag == 3: | ||||
|                 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: | ||||
|                 output[TLV_TYPES[tag]] = int.from_bytes(val) | ||||
|     return output | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/opt3001/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/opt3001/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										122
									
								
								esphome/components/opt3001/opt3001.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								esphome/components/opt3001/opt3001.cpp
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										27
									
								
								esphome/components/opt3001/opt3001.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								esphome/components/opt3001/opt3001.h
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										35
									
								
								esphome/components/opt3001/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								esphome/components/opt3001/sensor.py
									
									
									
									
									
										Normal 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) | ||||
| @@ -9,8 +9,8 @@ | ||||
| #include <hardware/dma.h> | ||||
| #include <hardware/irq.h> | ||||
| #include <hardware/pio.h> | ||||
| #include <pico/stdlib.h> | ||||
| #include <pico/sem.h> | ||||
| #include <pico/stdlib.h> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace rp2040_pio_led_strip { | ||||
| @@ -44,7 +44,7 @@ void RP2040PIOLEDStripLightOutput::setup() { | ||||
|  | ||||
|   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); | ||||
|   if (this->buf_ == nullptr) { | ||||
|     ESP_LOGE(TAG, "Failed to allocate buffer of size %u", buffer_size); | ||||
|   | ||||
| @@ -17,8 +17,8 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -65,6 +65,9 @@ _SELECT_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _SELECT_SCHEMA.add_extra(entity_duplicate_validator("select")) | ||||
|  | ||||
|  | ||||
| def select_schema( | ||||
|     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]): | ||||
|     await setup_entity(var, config) | ||||
|     await setup_entity(var, config, "select") | ||||
|  | ||||
|     cg.add(var.traits.set_options(options)) | ||||
|  | ||||
|   | ||||
| @@ -101,8 +101,8 @@ from esphome.const import ( | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
| from esphome.util import Registry | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| @@ -318,6 +318,8 @@ _SENSOR_SCHEMA = ( | ||||
|     ) | ||||
| ) | ||||
|  | ||||
| _SENSOR_SCHEMA.add_extra(entity_duplicate_validator("sensor")) | ||||
|  | ||||
|  | ||||
| def sensor_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
| @@ -787,7 +789,7 @@ async def build_filters(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: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|   | ||||
| @@ -79,6 +79,7 @@ CONF_SPI_MODE = "spi_mode" | ||||
| CONF_FORCE_SW = "force_sw" | ||||
| CONF_INTERFACE = "interface" | ||||
| CONF_INTERFACE_INDEX = "interface_index" | ||||
| CONF_RELEASE_DEVICE = "release_device" | ||||
| TYPE_SINGLE = "single" | ||||
| TYPE_QUAD = "quad" | ||||
| TYPE_OCTAL = "octal" | ||||
| @@ -378,6 +379,7 @@ def spi_device_schema( | ||||
|         cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( | ||||
|             SPI_MODE_OPTIONS, upper=True | ||||
|         ), | ||||
|         cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf), | ||||
|     } | ||||
|     if cs_pin_required: | ||||
|         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): | ||||
|     parent = await cg.get_variable(config[CONF_SPI_ID]) | ||||
|     cg.add(var.set_spi_parent(parent)) | ||||
|     if CONF_CS_PIN in config: | ||||
|         pin = await cg.gpio_pin_expression(config[CONF_CS_PIN]) | ||||
|     if cs_pin := config.get(CONF_CS_PIN): | ||||
|         pin = await cg.gpio_pin_expression(cs_pin) | ||||
|         cg.add(var.set_cs_pin(pin)) | ||||
|     if CONF_DATA_RATE in config: | ||||
|         cg.add(var.set_data_rate(config[CONF_DATA_RATE])) | ||||
|     if CONF_SPI_MODE in config: | ||||
|         cg.add(var.set_mode(config[CONF_SPI_MODE])) | ||||
|     if data_rate := config.get(CONF_DATA_RATE): | ||||
|         cg.add(var.set_data_rate(data_rate)) | ||||
|     if spi_mode := config.get(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): | ||||
|   | ||||
| @@ -16,12 +16,13 @@ bool SPIDelegate::is_ready() { return true; } | ||||
| 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, | ||||
|                                            GPIOPin *cs_pin) { | ||||
|                                            GPIOPin *cs_pin, bool release_device, bool write_only) { | ||||
|   if (this->devices_.count(device) != 0) { | ||||
|     ESP_LOGE(TAG, "Device already registered"); | ||||
|     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; | ||||
|   return delegate; | ||||
| } | ||||
|   | ||||
| @@ -317,7 +317,8 @@ class SPIBus { | ||||
|  | ||||
|   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_); | ||||
|   } | ||||
|  | ||||
| @@ -334,7 +335,7 @@ class SPIClient; | ||||
| class SPIComponent : public Component { | ||||
|  public: | ||||
|   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 set_clk(GPIOPin *clk) { this->clk_pin_ = clk; } | ||||
| @@ -390,7 +391,8 @@ class SPIClient { | ||||
|  | ||||
|   virtual void spi_setup() { | ||||
|     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() { | ||||
| @@ -399,6 +401,8 @@ class SPIClient { | ||||
|   } | ||||
|  | ||||
|   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: | ||||
|   SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST}; | ||||
| @@ -406,6 +410,8 @@ class SPIClient { | ||||
|   uint32_t data_rate_{1000000}; | ||||
|   SPIComponent *parent_{nullptr}; | ||||
|   GPIOPin *cs_{nullptr}; | ||||
|   bool release_device_{false}; | ||||
|   bool write_only_{false}; | ||||
|   SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -43,10 +43,7 @@ class SPIDelegateHw : public SPIDelegate { | ||||
|       return; | ||||
|     } | ||||
| #ifdef USE_RP2040 | ||||
|     // avoid overwriting the supplied buffer. Use vector for automatic deallocation | ||||
|     auto rxbuf = std::vector<uint8_t>(length); | ||||
|     memcpy(rxbuf.data(), ptr, length); | ||||
|     this->channel_->transfer((void *) rxbuf.data(), length); | ||||
|     this->channel_->transfer(ptr, nullptr, length); | ||||
| #elif defined(USE_ESP8266) | ||||
|     // 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 | ||||
| @@ -89,7 +86,8 @@ class SPIBusHw : public SPIBus { | ||||
| #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); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -11,34 +11,26 @@ static const size_t MAX_TRANSFER_SIZE = 4092;  // dictated by ESP-IDF API. | ||||
| class SPIDelegateHw : public SPIDelegate { | ||||
|  public: | ||||
|   SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, | ||||
|                 bool write_only) | ||||
|       : SPIDelegate(data_rate, bit_order, mode, cs_pin), channel_(channel), write_only_(write_only) { | ||||
|     spi_device_interface_config_t config = {}; | ||||
|     config.mode = static_cast<uint8_t>(mode); | ||||
|     config.clock_speed_hz = static_cast<int>(data_rate); | ||||
|     config.spics_io_num = -1; | ||||
|     config.flags = 0; | ||||
|     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 release_device, bool write_only) | ||||
|       : SPIDelegate(data_rate, bit_order, mode, cs_pin), | ||||
|         channel_(channel), | ||||
|         release_device_(release_device), | ||||
|         write_only_(write_only) { | ||||
|     if (!this->release_device_) | ||||
|       add_device_(); | ||||
|   } | ||||
|  | ||||
|   bool is_ready() override { return this->handle_ != nullptr; } | ||||
|  | ||||
|   void begin_transaction() override { | ||||
|     if (this->release_device_) | ||||
|       this->add_device_(); | ||||
|     if (this->is_ready()) { | ||||
|       if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK) | ||||
|         ESP_LOGE(TAG, "Failed to acquire SPI bus"); | ||||
|       SPIDelegate::begin_transaction(); | ||||
|     } 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()) { | ||||
|       SPIDelegate::end_transaction(); | ||||
|       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); } | ||||
|  | ||||
|  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_{}; | ||||
|   spi_device_handle_t handle_{}; | ||||
|   bool release_device_{false}; | ||||
|   bool write_only_{false}; | ||||
| }; | ||||
|  | ||||
| @@ -231,9 +249,10 @@ class SPIBusHw : public SPIBus { | ||||
|       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 { | ||||
|     return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, | ||||
|                              Utility::get_pin_no(this->sdi_pin_) == -1); | ||||
|   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, release_device, | ||||
|                              write_only || Utility::get_pin_no(this->sdi_pin_) == -1); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   | ||||
| @@ -5,7 +5,7 @@ namespace spi_led_strip { | ||||
|  | ||||
| SpiLedStrip::SpiLedStrip(uint16_t 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->buf_ = allocator.allocate(this->buffer_size_); | ||||
|   if (this->buf_ == nullptr) { | ||||
|   | ||||
| @@ -20,8 +20,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_SWITCH, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@esphome/core"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -91,6 +91,9 @@ _SWITCH_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _SWITCH_SCHEMA.add_extra(entity_duplicate_validator("switch")) | ||||
|  | ||||
|  | ||||
| def switch_schema( | ||||
|     class_: MockObjClass, | ||||
|     *, | ||||
| @@ -131,7 +134,7 @@ SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch")) | ||||
|  | ||||
|  | ||||
| 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: | ||||
|         cg.add(var.set_inverted(inverted)) | ||||
|   | ||||
| @@ -14,8 +14,8 @@ from esphome.const import ( | ||||
|     CONF_WEB_SERVER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@mauritskorse"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -58,6 +58,9 @@ _TEXT_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _TEXT_SCHEMA.add_extra(entity_duplicate_validator("text")) | ||||
|  | ||||
|  | ||||
| def text_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
|     *, | ||||
| @@ -94,7 +97,7 @@ async def setup_text_core_( | ||||
|     max_length: int | 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_max_length(max_length)) | ||||
|   | ||||
| @@ -21,8 +21,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_TIMESTAMP, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
| from esphome.util import Registry | ||||
|  | ||||
| DEVICE_CLASSES = [ | ||||
| @@ -153,6 +153,9 @@ _TEXT_SENSOR_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _TEXT_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("text_sensor")) | ||||
|  | ||||
|  | ||||
| def text_sensor_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
|     *, | ||||
| @@ -186,7 +189,7 @@ async def build_filters(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: | ||||
|         cg.add(var.set_device_class(device_class)) | ||||
|   | ||||
| @@ -15,8 +15,8 @@ from esphome.const import ( | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| CODEOWNERS = ["@jesserockz"] | ||||
| IS_PLATFORM_COMPONENT = True | ||||
| @@ -58,6 +58,9 @@ _UPDATE_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _UPDATE_SCHEMA.add_extra(entity_duplicate_validator("update")) | ||||
|  | ||||
|  | ||||
| def update_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
|     *, | ||||
| @@ -87,7 +90,7 @@ UPDATE_SCHEMA.add_extra(cv.deprecated_schema_constant("update")) | ||||
|  | ||||
|  | ||||
| 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): | ||||
|         cg.add(var.set_device_class(device_class_config)) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ from esphome.components.esp32 import ( | ||||
|     only_on_variant, | ||||
| ) | ||||
| 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 | ||||
|  | ||||
| AUTO_LOAD = ["bytebuffer"] | ||||
| @@ -16,9 +16,9 @@ usb_host_ns = cg.esphome_ns.namespace("usb_host") | ||||
| USBHost = usb_host_ns.class_("USBHost", Component) | ||||
| USBClient = usb_host_ns.class_("USBClient", Component) | ||||
|  | ||||
| CONF_DEVICES = "devices" | ||||
| CONF_VID = "vid" | ||||
| CONF_PID = "pid" | ||||
| CONF_ENABLE_HUBS = "enable_hubs" | ||||
|  | ||||
|  | ||||
| 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.GenerateID(): cv.declare_id(USBHost), | ||||
|             cv.Optional(CONF_ENABLE_HUBS, default=False): cv.boolean, | ||||
|             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): | ||||
|     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]) | ||||
|     await cg.register_component(var, config) | ||||
|     for device in config.get(CONF_DEVICES) or (): | ||||
|   | ||||
| @@ -22,8 +22,8 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_WATER, | ||||
| ) | ||||
| 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_helpers import setup_entity | ||||
|  | ||||
| IS_PLATFORM_COMPONENT = True | ||||
|  | ||||
| @@ -103,6 +103,9 @@ _VALVE_SCHEMA = ( | ||||
| ) | ||||
|  | ||||
|  | ||||
| _VALVE_SCHEMA.add_extra(entity_duplicate_validator("valve")) | ||||
|  | ||||
|  | ||||
| def valve_schema( | ||||
|     class_: MockObjClass = cv.UNDEFINED, | ||||
|     *, | ||||
| @@ -132,7 +135,7 @@ VALVE_SCHEMA.add_extra(cv.deprecated_schema_constant("valve")) | ||||
|  | ||||
|  | ||||
| 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): | ||||
|         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
		Reference in New Issue
	
	Block a user