mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Merge branch 'multi_device' into integration
This commit is contained in:
		| @@ -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; | ||||
|   | ||||
| @@ -1621,6 +1621,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; | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| """Helpers for config validation using voluptuous.""" | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| from contextlib import contextmanager | ||||
| from dataclasses import dataclass | ||||
| from datetime import datetime | ||||
| @@ -29,6 +31,7 @@ from esphome.const import ( | ||||
|     CONF_COMMAND_RETAIN, | ||||
|     CONF_COMMAND_TOPIC, | ||||
|     CONF_DAY, | ||||
|     CONF_DEVICE_ID, | ||||
|     CONF_DISABLED_BY_DEFAULT, | ||||
|     CONF_DISCOVERY, | ||||
|     CONF_ENTITY_CATEGORY, | ||||
| @@ -355,6 +358,13 @@ def icon(value): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def sub_device_id(value: str | None) -> core.ID: | ||||
|     # Lazy import to avoid circular imports | ||||
|     from esphome.core.config import Device | ||||
|  | ||||
|     return use_id(Device)(value) | ||||
|  | ||||
|  | ||||
| def boolean(value): | ||||
|     """Validate the given config option to be a boolean. | ||||
|  | ||||
| @@ -1896,6 +1906,7 @@ ENTITY_BASE_SCHEMA = Schema( | ||||
|         Optional(CONF_DISABLED_BY_DEFAULT, default=False): boolean, | ||||
|         Optional(CONF_ICON): icon, | ||||
|         Optional(CONF_ENTITY_CATEGORY): entity_category, | ||||
|         Optional(CONF_DEVICE_ID): sub_device_id, | ||||
|     } | ||||
| ) | ||||
|  | ||||
| @@ -1964,7 +1975,7 @@ class Version: | ||||
|         return f"{self.major}.{self.minor}.{self.patch}" | ||||
|  | ||||
|     @classmethod | ||||
|     def parse(cls, value: str) -> "Version": | ||||
|     def parse(cls, value: str) -> Version: | ||||
|         match = re.match(r"^(\d+).(\d+).(\d+)-?\w*$", value) | ||||
|         if match is None: | ||||
|             raise ValueError(f"Not a valid version number {value}") | ||||
|   | ||||
| @@ -56,6 +56,8 @@ CONF_AP = "ap" | ||||
| CONF_APPARENT_POWER = "apparent_power" | ||||
| CONF_ARDUINO_VERSION = "arduino_version" | ||||
| CONF_AREA = "area" | ||||
| CONF_AREA_ID = "area_id" | ||||
| CONF_AREAS = "areas" | ||||
| CONF_ARGS = "args" | ||||
| CONF_ASSUMED_STATE = "assumed_state" | ||||
| CONF_AT = "at" | ||||
| @@ -217,6 +219,7 @@ CONF_DEST = "dest" | ||||
| CONF_DEVICE = "device" | ||||
| CONF_DEVICE_CLASS = "device_class" | ||||
| CONF_DEVICE_FACTOR = "device_factor" | ||||
| CONF_DEVICE_ID = "device_id" | ||||
| CONF_DEVICES = "devices" | ||||
| CONF_DIELECTRIC_CONSTANT = "dielectric_constant" | ||||
| CONF_DIMENSIONS = "dimensions" | ||||
|   | ||||
| @@ -10,6 +10,13 @@ | ||||
| #include "esphome/core/runtime_stats.h" | ||||
| #include "esphome/core/scheduler.h" | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
| #include "esphome/core/device.h" | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
| #include "esphome/core/area.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_SOCKET_SELECT_SUPPORT | ||||
| #include <sys/select.h> | ||||
| #endif | ||||
| @@ -88,7 +95,7 @@ static const uint32_t TEARDOWN_TIMEOUT_REBOOT_MS = 1000;  // 1 second for quick | ||||
|  | ||||
| class Application { | ||||
|  public: | ||||
|   void pre_setup(const std::string &name, const std::string &friendly_name, const char *area, const char *comment, | ||||
|   void pre_setup(const std::string &name, const std::string &friendly_name, const char *comment, | ||||
|                  const char *compilation_time, bool name_add_mac_suffix) { | ||||
|     arch_init(); | ||||
|     this->name_add_mac_suffix_ = name_add_mac_suffix; | ||||
| @@ -103,11 +110,18 @@ class Application { | ||||
|       this->name_ = name; | ||||
|       this->friendly_name_ = friendly_name; | ||||
|     } | ||||
|     this->area_ = area; | ||||
|     // area is now handled through the areas system | ||||
|     this->comment_ = comment; | ||||
|     this->compilation_time_ = compilation_time; | ||||
|   } | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
|   void register_device(Device *device) { this->devices_.push_back(device); } | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   void register_area(Area *area) { this->areas_.push_back(area); } | ||||
| #endif | ||||
|  | ||||
|   void set_current_component(Component *component) { this->current_component_ = component; } | ||||
|   Component *get_current_component() { return this->current_component_; } | ||||
|  | ||||
| @@ -265,6 +279,12 @@ class Application { | ||||
| #ifdef USE_UPDATE | ||||
|   void reserve_update(size_t count) { this->updates_.reserve(count); } | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   void reserve_area(size_t count) { this->areas_.reserve(count); } | ||||
| #endif | ||||
| #ifdef USE_DEVICES | ||||
|   void reserve_device(size_t count) { this->devices_.reserve(count); } | ||||
| #endif | ||||
|  | ||||
|   /// Register the component in this Application instance. | ||||
|   template<class C> C *register_component(C *c) { | ||||
| @@ -286,7 +306,15 @@ class Application { | ||||
|   const std::string &get_friendly_name() const { return this->friendly_name_; } | ||||
|  | ||||
|   /// Get the area of this Application set by pre_setup(). | ||||
|   std::string get_area() const { return this->area_ == nullptr ? "" : this->area_; } | ||||
|   const char *get_area() const { | ||||
| #ifdef USE_AREAS | ||||
|     // If we have areas registered, return the name of the first one (which is the top-level area) | ||||
|     if (!this->areas_.empty() && this->areas_[0] != nullptr) { | ||||
|       return this->areas_[0]->get_name(); | ||||
|     } | ||||
| #endif | ||||
|     return ""; | ||||
|   } | ||||
|  | ||||
|   /// Get the comment of this Application set by pre_setup(). | ||||
|   std::string get_comment() const { return this->comment_; } | ||||
| @@ -347,6 +375,12 @@ class Application { | ||||
|  | ||||
|   uint8_t get_app_state() const { return this->app_state_; } | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
|   const std::vector<Device *> &get_devices() { return this->devices_; } | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   const std::vector<Area *> &get_areas() { return this->areas_; } | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   const std::vector<binary_sensor::BinarySensor *> &get_binary_sensors() { return this->binary_sensors_; } | ||||
|   binary_sensor::BinarySensor *get_binary_sensor_by_key(uint32_t key, bool include_internal = false) { | ||||
| @@ -623,6 +657,12 @@ class Application { | ||||
|   uint16_t current_loop_index_{0}; | ||||
|   bool in_loop_{false}; | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
|   std::vector<Device *> devices_{}; | ||||
| #endif | ||||
| #ifdef USE_AREAS | ||||
|   std::vector<Area *> areas_{}; | ||||
| #endif | ||||
| #ifdef USE_BINARY_SENSOR | ||||
|   std::vector<binary_sensor::BinarySensor *> binary_sensors_{}; | ||||
| #endif | ||||
| @@ -689,7 +729,6 @@ class Application { | ||||
|  | ||||
|   std::string name_; | ||||
|   std::string friendly_name_; | ||||
|   const char *area_{nullptr}; | ||||
|   const char *comment_{nullptr}; | ||||
|   const char *compilation_time_{nullptr}; | ||||
|   bool name_add_mac_suffix_; | ||||
|   | ||||
							
								
								
									
										19
									
								
								esphome/core/area.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/core/area.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| class Area { | ||||
|  public: | ||||
|   void set_area_id(uint32_t area_id) { this->area_id_ = area_id; } | ||||
|   uint32_t get_area_id() { return this->area_id_; } | ||||
|   void set_name(const char *name) { this->name_ = name; } | ||||
|   const char *get_name() { return this->name_; } | ||||
|  | ||||
|  protected: | ||||
|   uint32_t area_id_{}; | ||||
|   const char *name_ = ""; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
| @@ -1,18 +1,24 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| import logging | ||||
| import os | ||||
| from pathlib import Path | ||||
|  | ||||
| from esphome import automation | ||||
| from esphome import automation, core | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
|     CONF_AREA, | ||||
|     CONF_AREA_ID, | ||||
|     CONF_AREAS, | ||||
|     CONF_BUILD_PATH, | ||||
|     CONF_COMMENT, | ||||
|     CONF_COMPILE_PROCESS_LIMIT, | ||||
|     CONF_DEBUG_SCHEDULER, | ||||
|     CONF_DEVICES, | ||||
|     CONF_ESPHOME, | ||||
|     CONF_FRIENDLY_NAME, | ||||
|     CONF_ID, | ||||
|     CONF_INCLUDES, | ||||
|     CONF_LIBRARIES, | ||||
|     CONF_MIN_VERSION, | ||||
| @@ -32,7 +38,13 @@ from esphome.const import ( | ||||
|     __version__ as ESPHOME_VERSION, | ||||
| ) | ||||
| from esphome.core import CORE, coroutine_with_priority | ||||
| from esphome.helpers import copy_file_if_changed, get_str_env, walk_files | ||||
| from esphome.helpers import ( | ||||
|     copy_file_if_changed, | ||||
|     fnv1a_32bit_hash, | ||||
|     get_str_env, | ||||
|     walk_files, | ||||
| ) | ||||
| from esphome.types import ConfigType | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| @@ -48,7 +60,8 @@ LoopTrigger = cg.esphome_ns.class_( | ||||
| ProjectUpdateTrigger = cg.esphome_ns.class_( | ||||
|     "ProjectUpdateTrigger", cg.Component, automation.Trigger.template(cg.std_string) | ||||
| ) | ||||
|  | ||||
| Device = cg.esphome_ns.class_("Device") | ||||
| Area = cg.esphome_ns.class_("Area") | ||||
|  | ||||
| VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} | ||||
|  | ||||
| @@ -71,6 +84,56 @@ def validate_hostname(config): | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def validate_ids_and_references(config: ConfigType) -> ConfigType: | ||||
|     """Validate that there are no hash collisions between IDs and that area_id references are valid. | ||||
|  | ||||
|     This validation is critical because we use 32-bit hashes for performance on microcontrollers. | ||||
|     By detecting collisions at compile time, we prevent any runtime issues while maintaining | ||||
|     optimal performance on 32-bit platforms. In practice, with typical deployments having only | ||||
|     a handful of areas and devices, hash collisions are virtually impossible. | ||||
|     """ | ||||
|  | ||||
|     # Helper to check hash collisions | ||||
|     def check_hash_collision( | ||||
|         id_obj: core.ID, | ||||
|         hash_dict: dict[int, str], | ||||
|         item_type: str, | ||||
|         path: list[str | int], | ||||
|     ) -> None: | ||||
|         hash_val: int = fnv1a_32bit_hash(id_obj.id) | ||||
|         if hash_val in hash_dict and hash_dict[hash_val] != id_obj.id: | ||||
|             raise cv.Invalid( | ||||
|                 f"{item_type} ID '{id_obj.id}' with hash {hash_val} collides with " | ||||
|                 f"existing {item_type.lower()} ID '{hash_dict[hash_val]}'", | ||||
|                 path=path, | ||||
|             ) | ||||
|         hash_dict[hash_val] = id_obj.id | ||||
|  | ||||
|     # Collect all areas | ||||
|     all_areas: list[dict[str, str | core.ID]] = [] | ||||
|     if CONF_AREA in config: | ||||
|         all_areas.append(config[CONF_AREA]) | ||||
|     all_areas.extend(config[CONF_AREAS]) | ||||
|  | ||||
|     # Validate area hash collisions and collect IDs | ||||
|     area_hashes: dict[int, str] = {} | ||||
|     area_ids: set[str] = set() | ||||
|     for area in all_areas: | ||||
|         area_id: core.ID = area[CONF_ID] | ||||
|         check_hash_collision(area_id, area_hashes, "Area", [CONF_AREAS, area_id.id]) | ||||
|         area_ids.add(area_id.id) | ||||
|  | ||||
|     # Validate device hash collisions and area references | ||||
|     device_hashes: dict[int, str] = {} | ||||
|     for device in config[CONF_DEVICES]: | ||||
|         device_id: core.ID = device[CONF_ID] | ||||
|         check_hash_collision( | ||||
|             device_id, device_hashes, "Device", [CONF_DEVICES, device_id.id] | ||||
|         ) | ||||
|  | ||||
|     return config | ||||
|  | ||||
|  | ||||
| def valid_include(value): | ||||
|     # Look for "<...>" includes | ||||
|     if value.startswith("<") and value.endswith(">"): | ||||
| @@ -111,13 +174,32 @@ if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: | ||||
| else: | ||||
|     _compile_process_limit_default = cv.UNDEFINED | ||||
|  | ||||
| AREA_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_ID): cv.declare_id(Area), | ||||
|         cv.Required(CONF_NAME): cv.string, | ||||
|     } | ||||
| ) | ||||
|  | ||||
| DEVICE_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_ID): cv.declare_id(Device), | ||||
|         cv.Required(CONF_NAME): cv.string, | ||||
|         cv.Optional(CONF_AREA_ID): cv.use_id(Area), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| def validate_area_config(config: dict | str) -> dict[str, str | core.ID]: | ||||
|     return cv.maybe_simple_value(AREA_SCHEMA, key=CONF_NAME)(config) | ||||
|  | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.Required(CONF_NAME): cv.valid_name, | ||||
|             cv.Optional(CONF_FRIENDLY_NAME, ""): cv.string, | ||||
|             cv.Optional(CONF_AREA, ""): cv.string, | ||||
|             cv.Optional(CONF_AREA): validate_area_config, | ||||
|             cv.Optional(CONF_COMMENT): cv.string, | ||||
|             cv.Required(CONF_BUILD_PATH): cv.string, | ||||
|             cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema( | ||||
| @@ -167,11 +249,17 @@ CONFIG_SCHEMA = cv.All( | ||||
|             cv.Optional( | ||||
|                 CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default | ||||
|             ): cv.int_range(min=1, max=get_usable_cpu_count()), | ||||
|             cv.Optional(CONF_AREAS, default=[]): cv.ensure_list(AREA_SCHEMA), | ||||
|             cv.Optional(CONF_DEVICES, default=[]): cv.ensure_list(DEVICE_SCHEMA), | ||||
|         } | ||||
|     ), | ||||
|     validate_hostname, | ||||
| ) | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = cv.All(validate_ids_and_references) | ||||
|  | ||||
|  | ||||
| PRELOAD_CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.Required(CONF_NAME): cv.valid_name, | ||||
| @@ -336,7 +424,7 @@ async def _add_platform_reserves() -> None: | ||||
|  | ||||
|  | ||||
| @coroutine_with_priority(100.0) | ||||
| async def to_code(config): | ||||
| async def to_code(config: ConfigType) -> None: | ||||
|     cg.add_global(cg.global_ns.namespace("esphome").using) | ||||
|     # These can be used by user lambdas, put them to default scope | ||||
|     cg.add_global(cg.RawExpression("using std::isnan")) | ||||
| @@ -347,7 +435,6 @@ async def to_code(config): | ||||
|         cg.App.pre_setup( | ||||
|             config[CONF_NAME], | ||||
|             config[CONF_FRIENDLY_NAME], | ||||
|             config[CONF_AREA], | ||||
|             config.get(CONF_COMMENT, ""), | ||||
|             cg.RawExpression('__DATE__ ", " __TIME__'), | ||||
|             config[CONF_NAME_ADD_MAC_SUFFIX], | ||||
| @@ -417,3 +504,50 @@ async def to_code(config): | ||||
|  | ||||
|     if config[CONF_PLATFORMIO_OPTIONS]: | ||||
|         CORE.add_job(_add_platformio_options, config[CONF_PLATFORMIO_OPTIONS]) | ||||
|  | ||||
|     # Process areas | ||||
|     all_areas: list[dict[str, str | core.ID]] = [] | ||||
|     if CONF_AREA in config: | ||||
|         all_areas.append(config[CONF_AREA]) | ||||
|     all_areas.extend(config[CONF_AREAS]) | ||||
|  | ||||
|     if all_areas: | ||||
|         cg.add(cg.RawStatement(f"App.reserve_area({len(all_areas)});")) | ||||
|         cg.add_define("USE_AREAS") | ||||
|  | ||||
|         for area_conf in all_areas: | ||||
|             area_id: core.ID = area_conf[CONF_ID] | ||||
|             area_id_hash: int = fnv1a_32bit_hash(area_id.id) | ||||
|             area_name: str = area_conf[CONF_NAME] | ||||
|  | ||||
|             area_var = cg.new_Pvariable(area_id) | ||||
|             cg.add(area_var.set_area_id(area_id_hash)) | ||||
|             cg.add(area_var.set_name(area_name)) | ||||
|             cg.add(cg.App.register_area(area_var)) | ||||
|  | ||||
|     # Process devices | ||||
|     devices: list[dict[str, str | core.ID]] = config[CONF_DEVICES] | ||||
|     if not devices: | ||||
|         return | ||||
|  | ||||
|     # Reserve space for devices | ||||
|     cg.add(cg.RawStatement(f"App.reserve_device({len(devices)});")) | ||||
|     cg.add_define("USE_DEVICES") | ||||
|  | ||||
|     # Process each device | ||||
|     for dev_conf in devices: | ||||
|         device_id: core.ID = dev_conf[CONF_ID] | ||||
|         device_id_hash = fnv1a_32bit_hash(device_id.id) | ||||
|         device_name: str = dev_conf[CONF_NAME] | ||||
|  | ||||
|         dev = cg.new_Pvariable(device_id) | ||||
|         cg.add(dev.set_device_id(device_id_hash)) | ||||
|         cg.add(dev.set_name(device_name)) | ||||
|  | ||||
|         # Set area if specified | ||||
|         if CONF_AREA_ID in dev_conf: | ||||
|             area_id: core.ID = dev_conf[CONF_AREA_ID] | ||||
|             area_id_hash = fnv1a_32bit_hash(area_id.id) | ||||
|             cg.add(dev.set_area_id(area_id_hash)) | ||||
|  | ||||
|         cg.add(cg.App.register_device(dev)) | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
|  | ||||
| // Feature flags | ||||
| #define USE_ALARM_CONTROL_PANEL | ||||
| #define USE_AREAS | ||||
| #define USE_BINARY_SENSOR | ||||
| #define USE_BUTTON | ||||
| #define USE_CLIMATE | ||||
| @@ -29,6 +30,7 @@ | ||||
| #define USE_DATETIME_DATETIME | ||||
| #define USE_DATETIME_TIME | ||||
| #define USE_DEEP_SLEEP | ||||
| #define USE_DEVICES | ||||
| #define USE_DISPLAY | ||||
| #define USE_ESP32_IMPROV_STATE_CALLBACK | ||||
| #define USE_EVENT | ||||
|   | ||||
							
								
								
									
										20
									
								
								esphome/core/device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								esphome/core/device.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| class Device { | ||||
|  public: | ||||
|   void set_device_id(uint32_t device_id) { this->device_id_ = device_id; } | ||||
|   uint32_t get_device_id() { return this->device_id_; } | ||||
|   void set_name(const char *name) { this->name_ = name; } | ||||
|   const char *get_name() { return this->name_; } | ||||
|   void set_area_id(uint32_t area_id) { this->area_id_ = area_id; } | ||||
|   uint32_t get_area_id() { return this->area_id_; } | ||||
|  | ||||
|  protected: | ||||
|   uint32_t device_id_{}; | ||||
|   uint32_t area_id_{}; | ||||
|   const char *name_ = ""; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
| @@ -51,6 +51,12 @@ class EntityBase { | ||||
|   std::string get_icon() const; | ||||
|   void set_icon(const char *icon); | ||||
|  | ||||
| #ifdef USE_DEVICES | ||||
|   // Get/set this entity's device id | ||||
|   uint32_t get_device_id() const { return this->device_id_; } | ||||
|   void set_device_id(const uint32_t device_id) { this->device_id_ = device_id; } | ||||
| #endif | ||||
|  | ||||
|   // Check if this entity has state | ||||
|   bool has_state() const { return this->flags_.has_state; } | ||||
|  | ||||
| @@ -67,6 +73,9 @@ class EntityBase { | ||||
|   const char *object_id_c_str_{nullptr}; | ||||
|   const char *icon_c_str_{nullptr}; | ||||
|   uint32_t object_id_hash_{}; | ||||
| #ifdef USE_DEVICES | ||||
|   uint32_t device_id_{}; | ||||
| #endif | ||||
|  | ||||
|   // Bit-packed flags to save memory (1 byte instead of 5) | ||||
|   struct EntityFlags { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import logging | ||||
|  | ||||
| from esphome.const import ( | ||||
|     CONF_DEVICE_ID, | ||||
|     CONF_DISABLED_BY_DEFAULT, | ||||
|     CONF_ENTITY_CATEGORY, | ||||
|     CONF_ICON, | ||||
| @@ -16,7 +17,7 @@ from esphome.core import CORE, ID, coroutine | ||||
| from esphome.coroutine import FakeAwaitable | ||||
| from esphome.cpp_generator import add, get_variable | ||||
| from esphome.cpp_types import App | ||||
| from esphome.helpers import sanitize, snake_case | ||||
| from esphome.helpers import fnv1a_32bit_hash, sanitize, snake_case | ||||
| from esphome.types import ConfigFragmentType, ConfigType | ||||
| from esphome.util import Registry, RegistryEntry | ||||
|  | ||||
| @@ -110,6 +111,9 @@ async def setup_entity(var, config): | ||||
|         add(var.set_icon(config[CONF_ICON])) | ||||
|     if CONF_ENTITY_CATEGORY in config: | ||||
|         add(var.set_entity_category(config[CONF_ENTITY_CATEGORY])) | ||||
|     if CONF_DEVICE_ID in config: | ||||
|         device_id: ID = config[CONF_DEVICE_ID] | ||||
|         add(var.set_device_id(fnv1a_32bit_hash(device_id.id))) | ||||
|  | ||||
|  | ||||
| def extract_registry_entry_config( | ||||
|   | ||||
| @@ -1,25 +1,9 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| import unicodedata | ||||
|  | ||||
| from esphome.const import ALLOWED_NAME_CHARS | ||||
| from esphome.helpers import slugify | ||||
|  | ||||
|  | ||||
| def strip_accents(value): | ||||
|     return "".join( | ||||
|         c | ||||
|         for c in unicodedata.normalize("NFD", str(value)) | ||||
|         if unicodedata.category(c) != "Mn" | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def friendly_name_slugify(value): | ||||
|     value = ( | ||||
|         strip_accents(value) | ||||
|         .lower() | ||||
|         .replace(" ", "-") | ||||
|         .replace("_", "-") | ||||
|         .replace("--", "-") | ||||
|         .strip("-") | ||||
|     ) | ||||
|     return "".join(c for c in value if c in ALLOWED_NAME_CHARS) | ||||
| def friendly_name_slugify(value: str) -> str: | ||||
|     """Convert a friendly name to a slug with dashes instead of underscores.""" | ||||
|     # First use the standard slugify, then convert underscores to dashes | ||||
|     return slugify(value).replace("_", "-") | ||||
|   | ||||
| @@ -29,6 +29,53 @@ def ensure_unique_string(preferred_string, current_strings): | ||||
|     return test_string | ||||
|  | ||||
|  | ||||
| def fnv1a_32bit_hash(string: str) -> int: | ||||
|     """FNV-1a 32-bit hash function. | ||||
|  | ||||
|     Note: This uses 32-bit hash instead of 64-bit for several reasons: | ||||
|     1. ESPHome targets 32-bit microcontrollers with limited RAM (often <320KB) | ||||
|     2. Using 64-bit hashes would double the RAM usage for storing IDs | ||||
|     3. 64-bit operations are slower on 32-bit processors | ||||
|  | ||||
|     While there's a ~50% collision probability at ~77,000 unique IDs, | ||||
|     ESPHome validates for collisions at compile time, preventing any | ||||
|     runtime issues. In practice, most ESPHome installations only have | ||||
|     a handful of area_ids and device_ids (typically <10 areas and <100 | ||||
|     devices), making collisions virtually impossible. | ||||
|     """ | ||||
|     hash_value = 2166136261 | ||||
|     for char in string: | ||||
|         hash_value ^= ord(char) | ||||
|         hash_value = (hash_value * 16777619) & 0xFFFFFFFF | ||||
|     return hash_value | ||||
|  | ||||
|  | ||||
| def strip_accents(value: str) -> str: | ||||
|     """Remove accents from a string.""" | ||||
|     import unicodedata | ||||
|  | ||||
|     return "".join( | ||||
|         c | ||||
|         for c in unicodedata.normalize("NFD", str(value)) | ||||
|         if unicodedata.category(c) != "Mn" | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def slugify(value: str) -> str: | ||||
|     """Convert a string to a valid C++ identifier slug.""" | ||||
|     from esphome.const import ALLOWED_NAME_CHARS | ||||
|  | ||||
|     value = ( | ||||
|         strip_accents(value) | ||||
|         .lower() | ||||
|         .replace(" ", "_") | ||||
|         .replace("-", "_") | ||||
|         .replace("__", "_") | ||||
|         .strip("_") | ||||
|     ) | ||||
|     return "".join(c for c in value if c in ALLOWED_NAME_CHARS) | ||||
|  | ||||
|  | ||||
| def indent_all_but_first_and_last(text, padding="  "): | ||||
|     lines = text.splitlines(True) | ||||
|     if len(lines) <= 2: | ||||
|   | ||||
| @@ -2,7 +2,9 @@ esphome: | ||||
|   debug_scheduler: true | ||||
|   platformio_options: | ||||
|     board_build.flash_mode: dio | ||||
|   area: testing | ||||
|   area: | ||||
|     id: testing_area | ||||
|     name: Testing Area | ||||
|   on_boot: | ||||
|     logger.log: on_boot | ||||
|   on_shutdown: | ||||
| @@ -17,4 +19,20 @@ esphome: | ||||
|     version: "1.1" | ||||
|     on_update: | ||||
|       logger.log: on_update | ||||
|   areas: | ||||
|     - id: another_area | ||||
|       name: Another area | ||||
|   devices: | ||||
|     - id: other_device | ||||
|       name: Another device | ||||
|       area_id: another_area | ||||
|     - id: test_device | ||||
|       name: Test device in main area | ||||
|       area_id: testing_area  # Reference the main area (not in areas) | ||||
|     - id: no_area_device | ||||
|       name: Device without area  # This device has no area_id | ||||
|  | ||||
| binary_sensor: | ||||
|   - platform: template | ||||
|     name: Other device sensor | ||||
|     device_id: other_device | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| using namespace esphome; | ||||
|  | ||||
| void setup() { | ||||
|   App.pre_setup("livingroom", "LivingRoom", "LivingRoomArea", "comment", __DATE__ ", " __TIME__, false); | ||||
|   App.pre_setup("livingroom", "LivingRoom", "comment", __DATE__ ", " __TIME__, false); | ||||
|   auto *log = new logger::Logger(115200, 512);  // NOLINT | ||||
|   log->pre_setup(); | ||||
|   log->set_uart_selection(logger::UART_SELECTION_UART0); | ||||
|   | ||||
							
								
								
									
										57
									
								
								tests/integration/fixtures/areas_and_devices.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								tests/integration/fixtures/areas_and_devices.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| esphome: | ||||
|   name: areas-devices-test | ||||
|   # Define top-level area | ||||
|   area: | ||||
|     id: living_room_area | ||||
|     name: Living Room | ||||
|   # Define additional areas | ||||
|   areas: | ||||
|     - id: bedroom_area | ||||
|       name: Bedroom | ||||
|     - id: kitchen_area | ||||
|       name: Kitchen | ||||
|   # Define devices with area assignments | ||||
|   devices: | ||||
|     - id: light_controller_device | ||||
|       name: Light Controller | ||||
|       area_id: living_room_area  # Uses top-level area | ||||
|     - id: temp_sensor_device | ||||
|       name: Temperature Sensor | ||||
|       area_id: bedroom_area | ||||
|     - id: motion_detector_device | ||||
|       name: Motion Detector | ||||
|       area_id: living_room_area  # Reuses top-level area | ||||
|     - id: smart_switch_device | ||||
|       name: Smart Switch | ||||
|       area_id: kitchen_area | ||||
|  | ||||
| host: | ||||
| api: | ||||
| logger: | ||||
|  | ||||
| # Sensors assigned to different devices | ||||
| sensor: | ||||
|   - platform: template | ||||
|     name: Light Controller Sensor | ||||
|     device_id: light_controller_device | ||||
|     lambda: return 1.0; | ||||
|     update_interval: 0.1s | ||||
|  | ||||
|   - platform: template | ||||
|     name: Temperature Sensor Reading | ||||
|     device_id: temp_sensor_device | ||||
|     lambda: return 2.0; | ||||
|     update_interval: 0.1s | ||||
|  | ||||
|   - platform: template | ||||
|     name: Motion Detector Status | ||||
|     device_id: motion_detector_device | ||||
|     lambda: return 3.0; | ||||
|     update_interval: 0.1s | ||||
|  | ||||
|   - platform: template | ||||
|     name: Smart Switch Power | ||||
|     device_id: smart_switch_device | ||||
|     lambda: return 4.0; | ||||
|     update_interval: 0.1s | ||||
|  | ||||
							
								
								
									
										15
									
								
								tests/integration/fixtures/legacy_area.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests/integration/fixtures/legacy_area.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| esphome: | ||||
|   name: legacy-area-test | ||||
|   # Using legacy string-based area configuration | ||||
|   area: Master Bedroom | ||||
|  | ||||
| host: | ||||
| api: | ||||
| logger: | ||||
|  | ||||
| # Simple sensor to ensure the device compiles and runs | ||||
| sensor: | ||||
|   - platform: template | ||||
|     name: Test Sensor | ||||
|     lambda: return 42.0; | ||||
|     update_interval: 1s | ||||
							
								
								
									
										121
									
								
								tests/integration/test_areas_and_devices.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								tests/integration/test_areas_and_devices.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| """Integration test for areas and devices feature.""" | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| import asyncio | ||||
|  | ||||
| from aioesphomeapi import EntityState | ||||
| import pytest | ||||
|  | ||||
| from .types import APIClientConnectedFactory, RunCompiledFunction | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| async def test_areas_and_devices( | ||||
|     yaml_config: str, | ||||
|     run_compiled: RunCompiledFunction, | ||||
|     api_client_connected: APIClientConnectedFactory, | ||||
| ) -> None: | ||||
|     """Test areas and devices configuration with entity mapping.""" | ||||
|     async with run_compiled(yaml_config), api_client_connected() as client: | ||||
|         # Get device info which includes areas and devices | ||||
|         device_info = await client.device_info() | ||||
|         assert device_info is not None | ||||
|  | ||||
|         # Verify areas are reported | ||||
|         areas = device_info.areas | ||||
|         assert len(areas) >= 2, f"Expected at least 2 areas, got {len(areas)}" | ||||
|  | ||||
|         # Find our specific areas | ||||
|         main_area = next((a for a in areas if a.name == "Living Room"), None) | ||||
|         bedroom_area = next((a for a in areas if a.name == "Bedroom"), None) | ||||
|         kitchen_area = next((a for a in areas if a.name == "Kitchen"), None) | ||||
|  | ||||
|         assert main_area is not None, "Living Room area not found" | ||||
|         assert bedroom_area is not None, "Bedroom area not found" | ||||
|         assert kitchen_area is not None, "Kitchen area not found" | ||||
|  | ||||
|         # Verify devices are reported | ||||
|         devices = device_info.devices | ||||
|         assert len(devices) >= 4, f"Expected at least 4 devices, got {len(devices)}" | ||||
|  | ||||
|         # Find our specific devices | ||||
|         light_controller = next( | ||||
|             (d for d in devices if d.name == "Light Controller"), None | ||||
|         ) | ||||
|         temp_sensor = next((d for d in devices if d.name == "Temperature Sensor"), None) | ||||
|         motion_detector = next( | ||||
|             (d for d in devices if d.name == "Motion Detector"), None | ||||
|         ) | ||||
|         smart_switch = next((d for d in devices if d.name == "Smart Switch"), None) | ||||
|  | ||||
|         assert light_controller is not None, "Light Controller device not found" | ||||
|         assert temp_sensor is not None, "Temperature Sensor device not found" | ||||
|         assert motion_detector is not None, "Motion Detector device not found" | ||||
|         assert smart_switch is not None, "Smart Switch device not found" | ||||
|  | ||||
|         # Verify device area assignments | ||||
|         assert light_controller.area_id == main_area.area_id, ( | ||||
|             "Light Controller should be in Living Room" | ||||
|         ) | ||||
|         assert temp_sensor.area_id == bedroom_area.area_id, ( | ||||
|             "Temperature Sensor should be in Bedroom" | ||||
|         ) | ||||
|         assert motion_detector.area_id == main_area.area_id, ( | ||||
|             "Motion Detector should be in Living Room" | ||||
|         ) | ||||
|         assert smart_switch.area_id == kitchen_area.area_id, ( | ||||
|             "Smart Switch should be in Kitchen" | ||||
|         ) | ||||
|  | ||||
|         # Verify suggested_area is set to the top-level area name | ||||
|         assert device_info.suggested_area == "Living Room", ( | ||||
|             f"Expected suggested_area to be 'Living Room', got '{device_info.suggested_area}'" | ||||
|         ) | ||||
|  | ||||
|         # Get entity list to verify device_id mapping | ||||
|         entities = await client.list_entities_services() | ||||
|  | ||||
|         # Collect sensor entities | ||||
|         sensor_entities = [e for e in entities[0] if hasattr(e, "device_id")] | ||||
|         assert len(sensor_entities) >= 4, ( | ||||
|             f"Expected at least 4 sensor entities, got {len(sensor_entities)}" | ||||
|         ) | ||||
|  | ||||
|         # Subscribe to states to get sensor values | ||||
|         loop = asyncio.get_running_loop() | ||||
|         states: dict[int, EntityState] = {} | ||||
|         states_future: asyncio.Future[bool] = loop.create_future() | ||||
|  | ||||
|         def on_state(state: EntityState) -> None: | ||||
|             states[state.key] = state | ||||
|             # Check if we have all expected sensor states | ||||
|             if len(states) >= 4 and not states_future.done(): | ||||
|                 states_future.set_result(True) | ||||
|  | ||||
|         client.subscribe_states(on_state) | ||||
|  | ||||
|         # Wait for sensor states | ||||
|         try: | ||||
|             await asyncio.wait_for(states_future, timeout=10.0) | ||||
|         except asyncio.TimeoutError: | ||||
|             pytest.fail( | ||||
|                 f"Did not receive all sensor states within 10 seconds. " | ||||
|                 f"Received {len(states)} states" | ||||
|             ) | ||||
|  | ||||
|         # Verify we have sensor entities with proper device_id assignments | ||||
|         device_id_mapping = { | ||||
|             "Light Controller Sensor": light_controller.device_id, | ||||
|             "Temperature Sensor Reading": temp_sensor.device_id, | ||||
|             "Motion Detector Status": motion_detector.device_id, | ||||
|             "Smart Switch Power": smart_switch.device_id, | ||||
|         } | ||||
|  | ||||
|         for entity in sensor_entities: | ||||
|             if entity.name in device_id_mapping: | ||||
|                 expected_device_id = device_id_mapping[entity.name] | ||||
|                 assert entity.device_id == expected_device_id, ( | ||||
|                     f"{entity.name} has device_id {entity.device_id}, " | ||||
|                     f"expected {expected_device_id}" | ||||
|                 ) | ||||
							
								
								
									
										41
									
								
								tests/integration/test_legacy_area.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tests/integration/test_legacy_area.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| """Integration test for legacy string-based area configuration.""" | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from .types import APIClientConnectedFactory, RunCompiledFunction | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| async def test_legacy_area( | ||||
|     yaml_config: str, | ||||
|     run_compiled: RunCompiledFunction, | ||||
|     api_client_connected: APIClientConnectedFactory, | ||||
| ) -> None: | ||||
|     """Test legacy string-based area configuration.""" | ||||
|     async with run_compiled(yaml_config), api_client_connected() as client: | ||||
|         # Get device info which includes areas | ||||
|         device_info = await client.device_info() | ||||
|         assert device_info is not None | ||||
|  | ||||
|         # Verify the area is reported (should be converted to structured format) | ||||
|         areas = device_info.areas | ||||
|         assert len(areas) == 1, f"Expected exactly 1 area, got {len(areas)}" | ||||
|  | ||||
|         # Find the area - should be slugified from "Master Bedroom" | ||||
|         area = areas[0] | ||||
|         assert area.name == "Master Bedroom", ( | ||||
|             f"Expected area name 'Master Bedroom', got '{area.name}'" | ||||
|         ) | ||||
|  | ||||
|         # Verify area.id is set (it should be a hash) | ||||
|         assert area.area_id > 0, "Area ID should be a positive hash value" | ||||
|  | ||||
|         # The suggested_area field should be set for backward compatibility | ||||
|         assert device_info.suggested_area == "Master Bedroom", ( | ||||
|             f"Expected suggested_area to be 'Master Bedroom', got '{device_info.suggested_area}'" | ||||
|         ) | ||||
|  | ||||
|         # Verify deprecated warning would have been logged during compilation | ||||
|         # (We can't check logs directly in integration tests, but the code should work) | ||||
							
								
								
									
										256
									
								
								tests/unit_tests/core/test_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										256
									
								
								tests/unit_tests/core/test_config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,256 @@ | ||||
| """Unit tests for core config functionality including areas and devices.""" | ||||
|  | ||||
| from collections.abc import Callable | ||||
| from pathlib import Path | ||||
| from typing import Any | ||||
| from unittest.mock import patch | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| from esphome import config, config_validation as cv, core, yaml_util | ||||
| from esphome.config import Config | ||||
| from esphome.const import CONF_AREA, CONF_AREAS, CONF_DEVICES | ||||
| from esphome.core import CORE | ||||
| from esphome.core.config import Area, validate_area_config | ||||
|  | ||||
| FIXTURES_DIR = Path(__file__).parent.parent / "fixtures" / "core" / "config" | ||||
|  | ||||
|  | ||||
| @pytest.fixture | ||||
| def yaml_file(tmp_path: Path) -> Callable[[str], str]: | ||||
|     """Create a temporary YAML file for testing.""" | ||||
|  | ||||
|     def _yaml_file(content: str) -> str: | ||||
|         yaml_path = tmp_path / "test.yaml" | ||||
|         yaml_path.write_text(content) | ||||
|         return str(yaml_path) | ||||
|  | ||||
|     return _yaml_file | ||||
|  | ||||
|  | ||||
| @pytest.fixture(autouse=True) | ||||
| def reset_core(): | ||||
|     """Reset CORE after each test.""" | ||||
|     yield | ||||
|     CORE.reset() | ||||
|  | ||||
|  | ||||
| def load_config_from_yaml( | ||||
|     yaml_file: Callable[[str], str], yaml_content: str | ||||
| ) -> Config | None: | ||||
|     """Load configuration from YAML content.""" | ||||
|     yaml_path = yaml_file(yaml_content) | ||||
|     parsed_yaml = yaml_util.load_yaml(yaml_path) | ||||
|  | ||||
|     # Mock yaml_util.load_yaml to return our parsed content | ||||
|     with ( | ||||
|         patch.object(yaml_util, "load_yaml", return_value=parsed_yaml), | ||||
|         patch.object(CORE, "config_path", yaml_path), | ||||
|     ): | ||||
|         return config.read_config({}) | ||||
|  | ||||
|  | ||||
| def load_config_from_fixture( | ||||
|     yaml_file: Callable[[str], str], fixture_name: str | ||||
| ) -> Config | None: | ||||
|     """Load configuration from a fixture file.""" | ||||
|     fixture_path = FIXTURES_DIR / fixture_name | ||||
|     yaml_content = fixture_path.read_text() | ||||
|     return load_config_from_yaml(yaml_file, yaml_content) | ||||
|  | ||||
|  | ||||
| def test_validate_area_config_with_string() -> None: | ||||
|     """Test that string area config is converted to structured format.""" | ||||
|     result: dict[str, Any] = validate_area_config("Living Room") | ||||
|  | ||||
|     assert isinstance(result, dict) | ||||
|     assert "id" in result | ||||
|     assert "name" in result | ||||
|     assert result["name"] == "Living Room" | ||||
|     assert isinstance(result["id"], core.ID) | ||||
|     assert result["id"].is_declaration | ||||
|     assert not result["id"].is_manual | ||||
|  | ||||
|  | ||||
| def test_validate_area_config_with_dict() -> None: | ||||
|     """Test that structured area config passes through unchanged.""" | ||||
|     area_id = cv.declare_id(Area)("test_area") | ||||
|     input_config: dict[str, Any] = { | ||||
|         "id": area_id, | ||||
|         "name": "Test Area", | ||||
|     } | ||||
|  | ||||
|     result: dict[str, Any] = validate_area_config(input_config) | ||||
|  | ||||
|     assert result == input_config | ||||
|     assert result["id"] == area_id | ||||
|     assert result["name"] == "Test Area" | ||||
|  | ||||
|  | ||||
| def test_device_with_valid_area_id(yaml_file: Callable[[str], str]) -> None: | ||||
|     """Test that device with valid area_id works correctly.""" | ||||
|     result = load_config_from_fixture(yaml_file, "valid_area_device.yaml") | ||||
|     assert result is not None | ||||
|  | ||||
|     esphome_config = result["esphome"] | ||||
|  | ||||
|     # Verify areas were parsed correctly | ||||
|     assert CONF_AREAS in esphome_config | ||||
|     areas = esphome_config[CONF_AREAS] | ||||
|     assert len(areas) == 1 | ||||
|     assert areas[0]["id"].id == "bedroom_area" | ||||
|     assert areas[0]["name"] == "Bedroom" | ||||
|  | ||||
|     # Verify devices were parsed correctly | ||||
|     assert CONF_DEVICES in esphome_config | ||||
|     devices = esphome_config[CONF_DEVICES] | ||||
|     assert len(devices) == 1 | ||||
|     assert devices[0]["id"].id == "test_device" | ||||
|     assert devices[0]["name"] == "Test Device" | ||||
|     assert devices[0]["area_id"].id == "bedroom_area" | ||||
|  | ||||
|  | ||||
| def test_multiple_areas_and_devices(yaml_file: Callable[[str], str]) -> None: | ||||
|     """Test multiple areas and devices configuration.""" | ||||
|     result = load_config_from_fixture(yaml_file, "multiple_areas_devices.yaml") | ||||
|     assert result is not None | ||||
|  | ||||
|     esphome_config = result["esphome"] | ||||
|  | ||||
|     # Verify main area | ||||
|     assert CONF_AREA in esphome_config | ||||
|     main_area = esphome_config[CONF_AREA] | ||||
|     assert main_area["id"].id == "main_area" | ||||
|     assert main_area["name"] == "Main Area" | ||||
|  | ||||
|     # Verify additional areas | ||||
|     assert CONF_AREAS in esphome_config | ||||
|     areas = esphome_config[CONF_AREAS] | ||||
|     assert len(areas) == 2 | ||||
|     area_ids = {area["id"].id for area in areas} | ||||
|     assert area_ids == {"area1", "area2"} | ||||
|  | ||||
|     # Verify devices | ||||
|     assert CONF_DEVICES in esphome_config | ||||
|     devices = esphome_config[CONF_DEVICES] | ||||
|     assert len(devices) == 3 | ||||
|  | ||||
|     # Check device-area associations | ||||
|     device_area_map = {dev["id"].id: dev["area_id"].id for dev in devices} | ||||
|     assert device_area_map == { | ||||
|         "device1": "main_area", | ||||
|         "device2": "area1", | ||||
|         "device3": "area2", | ||||
|     } | ||||
|  | ||||
|  | ||||
| def test_legacy_string_area( | ||||
|     yaml_file: Callable[[str], str], caplog: pytest.LogCaptureFixture | ||||
| ) -> None: | ||||
|     """Test legacy string area configuration with deprecation warning.""" | ||||
|     result = load_config_from_fixture(yaml_file, "legacy_string_area.yaml") | ||||
|     assert result is not None | ||||
|  | ||||
|     esphome_config = result["esphome"] | ||||
|  | ||||
|     # Verify the string was converted to structured format | ||||
|     assert CONF_AREA in esphome_config | ||||
|     area = esphome_config[CONF_AREA] | ||||
|     assert isinstance(area, dict) | ||||
|     assert area["name"] == "Living Room" | ||||
|     assert isinstance(area["id"], core.ID) | ||||
|     assert area["id"].is_declaration | ||||
|     assert not area["id"].is_manual | ||||
|  | ||||
|  | ||||
| def test_area_id_collision( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
| ) -> None: | ||||
|     """Test that duplicate area IDs are detected.""" | ||||
|     result = load_config_from_fixture(yaml_file, "area_id_collision.yaml") | ||||
|     assert result is None | ||||
|  | ||||
|     # Check for the specific error message in stdout | ||||
|     captured = capsys.readouterr() | ||||
|     # Exact duplicates are now caught by IDPassValidationStep | ||||
|     assert "ID duplicate_id redefined! Check esphome->area->id." in captured.out | ||||
|  | ||||
|  | ||||
| def test_device_without_area(yaml_file: Callable[[str], str]) -> None: | ||||
|     """Test that devices without area_id work correctly.""" | ||||
|     result = load_config_from_fixture(yaml_file, "device_without_area.yaml") | ||||
|     assert result is not None | ||||
|  | ||||
|     esphome_config = result["esphome"] | ||||
|  | ||||
|     # Verify device was parsed | ||||
|     assert CONF_DEVICES in esphome_config | ||||
|     devices = esphome_config[CONF_DEVICES] | ||||
|     assert len(devices) == 1 | ||||
|  | ||||
|     device = devices[0] | ||||
|     assert device["id"].id == "test_device" | ||||
|     assert device["name"] == "Test Device" | ||||
|  | ||||
|     # Verify no area_id is present | ||||
|     assert "area_id" not in device | ||||
|  | ||||
|  | ||||
| def test_device_with_invalid_area_id( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
| ) -> None: | ||||
|     """Test that device with non-existent area_id fails validation.""" | ||||
|     result = load_config_from_fixture(yaml_file, "device_invalid_area.yaml") | ||||
|     assert result is None | ||||
|  | ||||
|     # Check for the specific error message in stdout | ||||
|     captured = capsys.readouterr() | ||||
|     print(captured.out) | ||||
|     assert ( | ||||
|         "Couldn't find ID 'nonexistent_area'. Please check you have defined an ID with that name in your configuration." | ||||
|         in captured.out | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_device_id_hash_collision( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
| ) -> None: | ||||
|     """Test that device IDs with hash collisions are detected.""" | ||||
|     result = load_config_from_fixture(yaml_file, "device_id_collision.yaml") | ||||
|     assert result is None | ||||
|  | ||||
|     # Check for the specific error message about hash collision | ||||
|     captured = capsys.readouterr() | ||||
|     # The error message shows the ID that collides and includes the hash value | ||||
|     assert ( | ||||
|         "Device ID 'd6ka' with hash 3082558663 collides with existing device ID 'test_2258'" | ||||
|         in captured.out | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_area_id_hash_collision( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
| ) -> None: | ||||
|     """Test that area IDs with hash collisions are detected.""" | ||||
|     result = load_config_from_fixture(yaml_file, "area_id_hash_collision.yaml") | ||||
|     assert result is None | ||||
|  | ||||
|     # Check for the specific error message about hash collision | ||||
|     captured = capsys.readouterr() | ||||
|     # The error message shows the ID that collides and includes the hash value | ||||
|     assert ( | ||||
|         "Area ID 'd6ka' with hash 3082558663 collides with existing area ID 'test_2258'" | ||||
|         in captured.out | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def test_device_duplicate_id( | ||||
|     yaml_file: Callable[[str], str], capsys: pytest.CaptureFixture[str] | ||||
| ) -> None: | ||||
|     """Test that duplicate device IDs are detected by IDPassValidationStep.""" | ||||
|     result = load_config_from_fixture(yaml_file, "device_duplicate_id.yaml") | ||||
|     assert result is None | ||||
|  | ||||
|     # Check for the specific error message from IDPassValidationStep | ||||
|     captured = capsys.readouterr() | ||||
|     assert "ID duplicate_device redefined!" in captured.out | ||||
							
								
								
									
										10
									
								
								tests/unit_tests/fixtures/core/config/area_id_collision.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/unit_tests/fixtures/core/config/area_id_collision.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| esphome: | ||||
|   name: test-collision | ||||
|   area: | ||||
|     id: duplicate_id | ||||
|     name: Area 1 | ||||
|   areas: | ||||
|     - id: duplicate_id | ||||
|       name: Area 2 | ||||
|  | ||||
| host: | ||||
| @@ -0,0 +1,10 @@ | ||||
| esphome: | ||||
|   name: test | ||||
|   areas: | ||||
|     - id: test_2258 | ||||
|       name: "Area 1" | ||||
|     - id: d6ka | ||||
|       name: "Area 2" | ||||
|  | ||||
| esp32: | ||||
|   board: esp32dev | ||||
| @@ -0,0 +1,10 @@ | ||||
| esphome: | ||||
|   name: test | ||||
|   devices: | ||||
|     - id: duplicate_device | ||||
|       name: "Device 1" | ||||
|     - id: duplicate_device | ||||
|       name: "Device 2" | ||||
|  | ||||
| esp32: | ||||
|   board: esp32dev | ||||
| @@ -0,0 +1,10 @@ | ||||
| esphome: | ||||
|   name: test | ||||
|   devices: | ||||
|     - id: test_2258 | ||||
|       name: "Device 1" | ||||
|     - id: d6ka | ||||
|       name: "Device 2" | ||||
|  | ||||
| esp32: | ||||
|   board: esp32dev | ||||
| @@ -0,0 +1,12 @@ | ||||
| esphome: | ||||
|   name: test | ||||
|   areas: | ||||
|     - id: valid_area | ||||
|       name: "Valid Area" | ||||
|   devices: | ||||
|     - id: test_device | ||||
|       name: "Test Device" | ||||
|       area_id: nonexistent_area | ||||
|  | ||||
| esp32: | ||||
|   board: esp32dev | ||||
| @@ -0,0 +1,7 @@ | ||||
| esphome: | ||||
|   name: test-device-no-area | ||||
|   devices: | ||||
|     - id: test_device | ||||
|       name: Test Device | ||||
|  | ||||
| host: | ||||
| @@ -0,0 +1,5 @@ | ||||
| esphome: | ||||
|   name: test-legacy-area | ||||
|   area: Living Room | ||||
|  | ||||
| host: | ||||
| @@ -0,0 +1,22 @@ | ||||
| esphome: | ||||
|   name: test-multiple | ||||
|   area: | ||||
|     id: main_area | ||||
|     name: Main Area | ||||
|   areas: | ||||
|     - id: area1 | ||||
|       name: Area 1 | ||||
|     - id: area2 | ||||
|       name: Area 2 | ||||
|   devices: | ||||
|     - id: device1 | ||||
|       name: Device 1 | ||||
|       area_id: main_area | ||||
|     - id: device2 | ||||
|       name: Device 2 | ||||
|       area_id: area1 | ||||
|     - id: device3 | ||||
|       name: Device 3 | ||||
|       area_id: area2 | ||||
|  | ||||
| host: | ||||
							
								
								
									
										11
									
								
								tests/unit_tests/fixtures/core/config/valid_area_device.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/unit_tests/fixtures/core/config/valid_area_device.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| esphome: | ||||
|   name: test-valid-area | ||||
|   areas: | ||||
|     - id: bedroom_area | ||||
|       name: Bedroom | ||||
|   devices: | ||||
|     - id: test_device | ||||
|       name: Test Device | ||||
|       area_id: bedroom_area | ||||
|  | ||||
| host: | ||||
		Reference in New Issue
	
	Block a user