Skip to content

Commit

Permalink
fix hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Oct 12, 2023
1 parent be24e8d commit c56a694
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 20 deletions.
8 changes: 4 additions & 4 deletions pyhap/accessory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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):
Expand Down Expand Up @@ -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"""
Expand Down
6 changes: 3 additions & 3 deletions pyhap/accessory_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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,
Expand Down
32 changes: 23 additions & 9 deletions pyhap/characteristic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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]:
Expand Down Expand Up @@ -303,15 +305,15 @@ 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)

if valid_values:
self._properties[PROP_VALID_VALUES] = valid_values

self._to_hap_cache = None

if self._always_null:
self.value = None
return
Expand All @@ -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.
Expand Down Expand Up @@ -393,16 +400,20 @@ 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.
: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]
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pyhap/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_characteristic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit c56a694

Please sign in to comment.