diff --git a/pyhap/accessory.py b/pyhap/accessory.py index 6dc97309..f8a95f57 100644 --- a/pyhap/accessory.py +++ b/pyhap/accessory.py @@ -222,7 +222,7 @@ def get_characteristic(self, aid: int, iid: int) -> Optional["Characteristic"]: return self.iid_manager.get_obj(iid) - def to_HAP(self) -> Dict[str, Any]: + def to_HAP(self, include_value: bool = True) -> Dict[str, Any]: """A HAP representation of this Accessory. :return: A HAP representation of this accessory. For example: @@ -241,7 +241,7 @@ def to_HAP(self) -> Dict[str, Any]: """ return { HAP_REPR_AID: self.aid, - HAP_REPR_SERVICES: [s.to_HAP() for s in self.services], + HAP_REPR_SERVICES: [s.to_HAP(include_value=include_value) for s in self.services], } def setup_message(self): @@ -386,12 +386,12 @@ def add_accessory(self, acc: "Accessory") -> None: self.accessories[acc.aid] = acc - def to_HAP(self) -> List[Dict[str, Any]]: + def to_HAP(self, include_value: bool = True) -> List[Dict[str, Any]]: """Returns a HAP representation of itself and all contained accessories. .. seealso:: Accessory.to_HAP """ - return [acc.to_HAP() for acc in (super(), *self.accessories.values())] + return [acc.to_HAP(include_value=include_value) for acc in (super(), *self.accessories.values())] def get_characteristic(self, aid: int, iid: int) -> Optional["Characteristic"]: """.. seealso:: Accessory.to_HAP""" diff --git a/pyhap/accessory_driver.py b/pyhap/accessory_driver.py index c767f590..952eb43e 100644 --- a/pyhap/accessory_driver.py +++ b/pyhap/accessory_driver.py @@ -746,10 +746,10 @@ def setup_srp_verifier(self): def accessories_hash(self): """Hash the get_accessories response to track configuration changes.""" return hashlib.sha512( - util.to_sorted_hap_json(self.get_accessories()) + util.to_sorted_hap_json(self.get_accessories(include_value=False)) ).hexdigest() - def get_accessories(self): + def get_accessories(self, include_value: bool = True): """Returns the accessory in HAP format. :return: An example HAP representation is: @@ -774,7 +774,7 @@ def get_accessories(self): :rtype: dict """ - hap_rep = self.accessory.to_HAP() + hap_rep = self.accessory.to_HAP(include_value=include_value) if not isinstance(hap_rep, list): hap_rep = [ hap_rep, diff --git a/pyhap/characteristic.py b/pyhap/characteristic.py index 0571e10a..5a361fd9 100644 --- a/pyhap/characteristic.py +++ b/pyhap/characteristic.py @@ -136,6 +136,7 @@ class Characteristic: "_loader_display_name", "allow_invalid_client_values", "unique_id", + "_to_hap_cache_with_value", "_to_hap_cache", "_always_null", ) @@ -183,6 +184,7 @@ def __init__( self.unique_id = unique_id self._uuid_str = uuid_to_hap_type(type_id) self._loader_display_name: Optional[str] = None + self._to_hap_cache_with_value: Optional[Dict[str, Any]] = None self._to_hap_cache: Optional[Dict[str, Any]] = None @property @@ -194,7 +196,7 @@ def display_name(self) -> Optional[str]: def display_name(self, value: str) -> None: """Set the display name of the characteristic.""" self._display_name = value - self._to_hap_cache = None + self._clear_cache() @property def value(self) -> Any: @@ -205,7 +207,7 @@ def value(self) -> Any: def value(self, value: Any) -> None: """Set the value of the characteristic.""" self._value = value - self._to_hap_cache = None + self._clear_cache() @property def properties(self) -> Dict[str, Any]: @@ -303,6 +305,8 @@ def override_properties( if not properties and not valid_values: raise ValueError("No properties or valid_values specified to override.") + self._clear_cache() + if properties: _validate_properties(properties) self._properties.update(properties) @@ -310,8 +314,6 @@ def override_properties( if valid_values: self._properties[PROP_VALID_VALUES] = valid_values - self._to_hap_cache = None - if self._always_null: self.value = None return @@ -322,6 +324,11 @@ def override_properties( except ValueError: self.value = self._get_default_value() + def _clear_cache(self) -> None: + """Clear the cached HAP representation.""" + self._to_hap_cache = None + self._to_hap_cache_with_value = None + def set_value(self, value: Any, should_notify: bool = True) -> None: """Set the given raw value. It is checked if it is a valid value. @@ -393,7 +400,7 @@ def notify(self, sender_client_addr: Optional[Tuple[str, int]] = None) -> None: self.broker.publish(self.value, self, sender_client_addr, immediate) # pylint: disable=invalid-name - def to_HAP(self) -> Dict[str, Any]: + def to_HAP(self, include_value: bool = True) -> Dict[str, Any]: """Create a HAP representation of this Characteristic. Used for json serialization. @@ -401,8 +408,12 @@ def to_HAP(self) -> Dict[str, Any]: :return: A HAP representation. :rtype: dict """ - if self._to_hap_cache is not None: - return self._to_hap_cache + if include_value: + if self._to_hap_cache_with_value is not None: + return self._to_hap_cache_with_value + else: + if self._to_hap_cache is not None: + return self._to_hap_cache properties = self._properties permissions = properties[PROP_PERMISSIONS] @@ -435,10 +446,13 @@ def to_HAP(self) -> Dict[str, Any]: if max_length != DEFAULT_MAX_LENGTH: hap_rep[HAP_REPR_MAX_LEN] = max_length - if HAP_PERMISSION_READ in permissions: + if include_value and HAP_PERMISSION_READ in permissions: hap_rep[HAP_REPR_VALUE] = self.get_value() - self._to_hap_cache = hap_rep + if include_value: + self._to_hap_cache_with_value = hap_rep + else: + self._to_hap_cache = hap_rep return hap_rep @classmethod diff --git a/pyhap/service.py b/pyhap/service.py index 1579809c..e31b3920 100644 --- a/pyhap/service.py +++ b/pyhap/service.py @@ -117,7 +117,7 @@ def configure_char( return char # pylint: disable=invalid-name - def to_HAP(self) -> Dict[str, Any]: + def to_HAP(self, include_value: bool = True) -> Dict[str, Any]: """Create a HAP representation of this Service. :return: A HAP representation. @@ -126,7 +126,7 @@ def to_HAP(self) -> Dict[str, Any]: hap = { HAP_REPR_IID: self.broker.iid_manager.get_iid(self), HAP_REPR_TYPE: self._uuid_str, - HAP_REPR_CHARS: [c.to_HAP() for c in self.characteristics], + HAP_REPR_CHARS: [c.to_HAP(include_value) for c in self.characteristics], } if self.is_primary_service is not None: diff --git a/tests/test_characteristic.py b/tests/test_characteristic.py index abafa8b1..d952b72f 100644 --- a/tests/test_characteristic.py +++ b/tests/test_characteristic.py @@ -476,13 +476,13 @@ def test_to_HAP_bool(): # pylint: disable=protected-access char = get_char(PROPERTIES.copy()) char.properties["Format"] = "bool" - char._to_hap_cache = None + char._clear_cache() with patch.object(char, "broker"): hap_repr = char.to_HAP() assert hap_repr["format"] == "bool" char.properties["Permissions"] = [] - char._to_hap_cache = None + char._clear_cache() with patch.object(char, "broker"): hap_repr = char.to_HAP() assert "value" not in hap_repr