diff --git a/miio/miot_models.py b/miio/miot_models.py index 1269946d5..6f4abfe59 100644 --- a/miio/miot_models.py +++ b/miio/miot_models.py @@ -1,4 +1,5 @@ import logging +from abc import abstractmethod from datetime import timedelta from enum import Enum from typing import Any, Optional @@ -150,6 +151,11 @@ def normalized_name(self) -> str: """ return self.name.replace(":", "_").replace("-", "_") + @property + @abstractmethod + def unique_identifier(self) -> str: + """Return unique identifier.""" + class MiotAction(MiotBaseModel): """Action presentation for miot.""" @@ -176,8 +182,6 @@ def fill_from_parent(self, service: "MiotService"): def get_descriptor(self): """Create a descriptor based on the property information.""" - id_ = self.name - extras = self.extras extras["urn"] = self.urn extras["siid"] = self.siid @@ -190,12 +194,17 @@ def get_descriptor(self): inputs = [prop.get_descriptor() for prop in self.inputs] return ActionDescriptor( - id=id_, + id=self.unique_identifier, name=self.description, inputs=inputs, extras=extras, ) + @property + def unique_identifier(self) -> str: + """Return unique identifier.""" + return f"{self.normalized_name}_{self.siid}_{self.aiid}" + class Config: extra = "forbid" @@ -327,7 +336,7 @@ def _create_enum_descriptor(self) -> EnumDescriptor: raise desc = EnumDescriptor( - id=self.name, + id=self.unique_identifier, name=self.description, status_attribute=self.normalized_name, unit=self.unit, @@ -346,7 +355,7 @@ def _create_range_descriptor( if self.range is None: raise ValueError("Range is None") desc = RangeDescriptor( - id=self.name, + id=self.unique_identifier, name=self.description, status_attribute=self.normalized_name, min_value=self.range[0], @@ -363,7 +372,7 @@ def _create_range_descriptor( def _create_regular_descriptor(self) -> PropertyDescriptor: """Create boolean setting descriptor.""" return PropertyDescriptor( - id=self.name, + id=self.unique_identifier, name=self.description, status_attribute=self.normalized_name, type=self.format, @@ -371,6 +380,11 @@ def _create_regular_descriptor(self) -> PropertyDescriptor: access=self._miot_access_list_to_access(self.access), ) + @property + def unique_identifier(self) -> str: + """Return unique identifier.""" + return f"{self.normalized_name}_{self.siid}_{self.piid}" + class Config: extra = "forbid" @@ -381,6 +395,11 @@ class MiotEvent(MiotBaseModel): eiid: int = Field(alias="iid") arguments: Any + @property + def unique_identifier(self) -> str: + """Return unique identifier.""" + return f"{self.normalized_name}_{self.siid}_{self.eiid}" + class Config: extra = "forbid" diff --git a/miio/tests/test_miot_models.py b/miio/tests/test_miot_models.py index 32afb76aa..046ad2a08 100644 --- a/miio/tests/test_miot_models.py +++ b/miio/tests/test_miot_models.py @@ -21,6 +21,7 @@ URN, MiotAccess, MiotAction, + MiotBaseModel, MiotEnumValue, MiotEvent, MiotFormat, @@ -349,3 +350,18 @@ def test_get_descriptor_enum_property(read_only, expected): def test_property_pretty_value(): """Test the pretty value conversions.""" raise NotImplementedError() + + +@pytest.mark.parametrize( + ("collection", "id_var"), + [("actions", "aiid"), ("properties", "piid"), ("events", "eiid")], +) +def test_unique_identifier(collection, id_var): + """Test unique identifier for properties, actions, and events.""" + serv = MiotService.parse_raw(DUMMY_SERVICE) + elem: MiotBaseModel = getattr(serv, collection) + first = elem[0] + assert ( + first.unique_identifier + == f"{first.normalized_name}_{serv.siid}_{getattr(first, id_var)}" + )