Skip to content

Commit 9a54ecf

Browse files
authored
Merge pull request #424 from bdraco/ios16_paring
2 parents fbd1f4b + 5d3bd54 commit 9a54ecf

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

pyhap/accessory_driver.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -626,13 +626,15 @@ def async_persist(self):
626626
Must be run in the event loop.
627627
"""
628628
loop = asyncio.get_event_loop()
629+
logger.debug("Scheduling write of accessory state to disk")
629630
asyncio.ensure_future(loop.run_in_executor(None, self.persist))
630631

631632
def persist(self):
632633
"""Saves the state of the accessory.
633634
634635
Must run in executor.
635636
"""
637+
logger.debug("Writing of accessory state to disk")
636638
tmp_filename = None
637639
try:
638640
temp_dir = os.path.dirname(self.persist_file)
@@ -642,6 +644,9 @@ def persist(self):
642644
tmp_filename = file_handle.name
643645
self.encoder.persist(file_handle, self.state)
644646
os.replace(tmp_filename, self.persist_file)
647+
except Exception: # pylint: disable=broad-except
648+
logger.exception("Failed to persist accessory state")
649+
raise
645650
finally:
646651
if tmp_filename and os.path.exists(tmp_filename):
647652
os.remove(tmp_filename)
@@ -672,7 +677,9 @@ def pair(self, client_uuid, client_public, client_permissions):
672677
:return: Whether the pairing is successful.
673678
:rtype: bool
674679
"""
675-
logger.info("Paired with %s.", client_uuid)
680+
logger.info(
681+
"Paired with %s with permissions %s.", client_uuid, client_permissions
682+
)
676683
self.state.add_paired_client(client_uuid, client_public, client_permissions)
677684
self.async_persist()
678685
return True

pyhap/hap_handler.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable as ChaCha20Poly1305
1515

1616
from pyhap import tlv
17+
1718
from pyhap.const import (
1819
CATEGORY_BRIDGE,
1920
HAP_PERMISSIONS,
@@ -416,7 +417,11 @@ def _pairing_five(self, client_username, client_ltpk, encryption_key):
416417
cipher = ChaCha20Poly1305(encryption_key)
417418
aead_message = bytes(cipher.encrypt(self.PAIRING_5_NONCE, bytes(message), b""))
418419

419-
client_uuid = uuid.UUID(str(client_username, "utf-8"))
420+
client_username_str = str(client_username, "utf-8")
421+
client_uuid = uuid.UUID(client_username_str)
422+
logger.debug(
423+
"Finishing pairing with admin %s uuid=%s", client_username_str, client_uuid
424+
)
420425
should_confirm = self.accessory_handler.pair(
421426
client_uuid, client_ltpk, HAP_PERMISSIONS.ADMIN
422427
)
@@ -668,11 +673,18 @@ def handle_pairings(self):
668673

669674
def _handle_add_pairing(self, tlv_objects):
670675
"""Update client information."""
671-
logger.debug("%s: Adding client pairing.", self.client_address)
672676
client_username = tlv_objects[HAP_TLV_TAGS.USERNAME]
677+
client_username_str = str(client_username, "utf-8")
673678
client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY]
674679
permissions = tlv_objects[HAP_TLV_TAGS.PERMISSIONS]
675-
client_uuid = uuid.UUID(str(client_username, "utf-8"))
680+
client_uuid = uuid.UUID(client_username_str)
681+
logger.debug(
682+
"%s: Adding client pairing for %s uuid=%s with permissions %s.",
683+
self.client_address,
684+
client_username_str,
685+
client_uuid,
686+
permissions,
687+
)
676688
should_confirm = self.accessory_handler.pair(
677689
client_uuid, client_public, permissions
678690
)
@@ -685,10 +697,17 @@ def _handle_add_pairing(self, tlv_objects):
685697

686698
def _handle_remove_pairing(self, tlv_objects):
687699
"""Remove pairing with the client."""
688-
logger.debug("%s: Removing client pairing.", self.client_address)
689700
client_username = tlv_objects[HAP_TLV_TAGS.USERNAME]
690-
client_uuid = uuid.UUID(str(client_username, "utf-8"))
701+
client_username_str = str(client_username, "utf-8")
702+
client_uuid = uuid.UUID(client_username_str)
691703
was_paired = self.state.paired
704+
logger.debug(
705+
"%s: Removing client pairing (%s) uuid=%s (was previously paired=%s).",
706+
self.client_address,
707+
client_username_str,
708+
client_uuid,
709+
was_paired,
710+
)
692711
# If the client does not exist, we must
693712
# respond with success per the spec
694713
if client_uuid in self.state.paired_clients:
@@ -713,7 +732,10 @@ def _handle_list_pairings(self):
713732
response.extend(
714733
[
715734
HAP_TLV_TAGS.USERNAME,
716-
str(client_uuid).encode("utf-8"),
735+
# iOS 16+ requires the username to be uppercase
736+
# or it will unpair the accessory because it thinks
737+
# the username is invalid
738+
str(client_uuid).encode("utf-8").upper(),
717739
HAP_TLV_TAGS.PUBLIC_KEY,
718740
client_public,
719741
HAP_TLV_TAGS.PERMISSIONS,

tests/test_hap_handler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_list_pairings(driver):
7171

7272
assert tlv_objects == {
7373
hap_handler.HAP_TLV_TAGS.SEQUENCE_NUM: hap_handler.HAP_TLV_STATES.M2,
74-
hap_handler.HAP_TLV_TAGS.USERNAME: str(CLIENT_UUID).encode("utf8"),
74+
hap_handler.HAP_TLV_TAGS.USERNAME: str(CLIENT_UUID).encode("utf8").upper(),
7575
hap_handler.HAP_TLV_TAGS.PUBLIC_KEY: PUBLIC_KEY,
7676
hap_handler.HAP_TLV_TAGS.PERMISSIONS: hap_handler.HAP_PERMISSIONS.ADMIN,
7777
}

0 commit comments

Comments
 (0)