Skip to content

Commit

Permalink
Fix pairing with iOS 16
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco committed Oct 14, 2022
1 parent fbd1f4b commit 3597484
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 7 deletions.
8 changes: 7 additions & 1 deletion pyhap/accessory_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,13 +626,15 @@ def async_persist(self):
Must be run in the event loop.
"""
loop = asyncio.get_event_loop()
logger.debug("Scheduling write of accessory state to disk")
asyncio.ensure_future(loop.run_in_executor(None, self.persist))

def persist(self):
"""Saves the state of the accessory.
Must run in executor.
"""
logger.debug("Writing of accessory state to disk")
tmp_filename = None
try:
temp_dir = os.path.dirname(self.persist_file)
Expand All @@ -642,6 +644,8 @@ def persist(self):
tmp_filename = file_handle.name
self.encoder.persist(file_handle, self.state)
os.replace(tmp_filename, self.persist_file)
except Exception: # pylint: disable=broad-except
logger.exception("Failed to persist accessory state")
finally:
if tmp_filename and os.path.exists(tmp_filename):
os.remove(tmp_filename)
Expand Down Expand Up @@ -672,7 +676,9 @@ def pair(self, client_uuid, client_public, client_permissions):
:return: Whether the pairing is successful.
:rtype: bool
"""
logger.info("Paired with %s.", client_uuid)
logger.info(
"Paired with %s with permissions %s.", client_uuid, client_permissions
)
self.state.add_paired_client(client_uuid, client_public, client_permissions)
self.async_persist()
return True
Expand Down
34 changes: 28 additions & 6 deletions pyhap/hap_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable as ChaCha20Poly1305

from pyhap import tlv

from pyhap.const import (
CATEGORY_BRIDGE,
HAP_PERMISSIONS,
Expand Down Expand Up @@ -416,7 +417,11 @@ def _pairing_five(self, client_username, client_ltpk, encryption_key):
cipher = ChaCha20Poly1305(encryption_key)
aead_message = bytes(cipher.encrypt(self.PAIRING_5_NONCE, bytes(message), b""))

client_uuid = uuid.UUID(str(client_username, "utf-8"))
client_username_str = str(client_username, "utf-8")
client_uuid = uuid.UUID(client_username_str)
logger.debug(
"Finishing pairing with admin %s uuid=%s", client_username_str, client_uuid
)
should_confirm = self.accessory_handler.pair(
client_uuid, client_ltpk, HAP_PERMISSIONS.ADMIN
)
Expand Down Expand Up @@ -668,11 +673,18 @@ def handle_pairings(self):

def _handle_add_pairing(self, tlv_objects):
"""Update client information."""
logger.debug("%s: Adding client pairing.", self.client_address)
client_username = tlv_objects[HAP_TLV_TAGS.USERNAME]
client_username_str = str(client_username, "utf-8")
client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY]
permissions = tlv_objects[HAP_TLV_TAGS.PERMISSIONS]
client_uuid = uuid.UUID(str(client_username, "utf-8"))
client_uuid = uuid.UUID(client_username_str)
logger.debug(
"%s: Adding client pairing for %s uuid=%s with permissions %s.",
self.client_address,
client_username_str,
client_uuid,
permissions,
)
should_confirm = self.accessory_handler.pair(
client_uuid, client_public, permissions
)
Expand All @@ -685,10 +697,17 @@ def _handle_add_pairing(self, tlv_objects):

def _handle_remove_pairing(self, tlv_objects):
"""Remove pairing with the client."""
logger.debug("%s: Removing client pairing.", self.client_address)
client_username = tlv_objects[HAP_TLV_TAGS.USERNAME]
client_uuid = uuid.UUID(str(client_username, "utf-8"))
client_username_str = str(client_username, "utf-8")
client_uuid = uuid.UUID(client_username_str)
was_paired = self.state.paired
logger.debug(
"%s: Removing client pairing (%s) uuid=%s (was previously paired=%s).",
self.client_address,
client_username_str,
client_uuid,
was_paired,
)
# If the client does not exist, we must
# respond with success per the spec
if client_uuid in self.state.paired_clients:
Expand All @@ -713,7 +732,10 @@ def _handle_list_pairings(self):
response.extend(
[
HAP_TLV_TAGS.USERNAME,
str(client_uuid).encode("utf-8"),
# iOS 16+ requires the username to be uppercase
# or it will unpair the accessory because it thinks
# the username is invalid
str(client_uuid).encode("utf-8").upper(),
HAP_TLV_TAGS.PUBLIC_KEY,
client_public,
HAP_TLV_TAGS.PERMISSIONS,
Expand Down

0 comments on commit 3597484

Please sign in to comment.