diff --git a/aiohomekit/controller/coap/connection.py b/aiohomekit/controller/coap/connection.py index d1b58f54..3ea94cc5 100644 --- a/aiohomekit/controller/coap/connection.py +++ b/aiohomekit/controller/coap/connection.py @@ -19,6 +19,7 @@ import asyncio import logging import random +import socket import struct from typing import Any import uuid @@ -170,7 +171,13 @@ async def post_bytes(self, payload: bytes, timeout: int = 16.0): except (NetworkError, asyncio.TimeoutError): raise AccessoryDisconnectedError("Request timeout") - if response.code != Code.CHANGED: + if response.code == Code.NOT_FOUND: + # maybe the accessory lost power or was otherwise rebooted + logger.error("CoAP POST returned 404, our session is gone.") + await self.coap_ctx.shutdown() + self.coap_ctx = None + raise AccessoryDisconnectedError("Accessory lost our session") + elif response.code != Code.CHANGED: logger.warning(f"CoAP POST returned unexpected code {response}") return await self._decrypt_response(response) @@ -244,8 +251,10 @@ def __init__(self, owner, host, port): self.address = f"[{host}]:{port}" self.connection_lock = asyncio.Lock() self.enc_ctx = None + self.host = host self.owner = owner self.pair_setup_client = None + self.port = port async def reconnect_soon(self): if not self.enc_ctx: @@ -322,14 +331,29 @@ async def do_pair_setup_finish(self, pin, salt, srpB): return pairing + def _get_local_ipv6_addr(self, remote): + s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + s.connect(remote) + addr = s.getsockname()[0] + s.close() + return addr + async def do_pair_verify(self, pairing_data): if self.is_connected: logger.warning("Connecting to connected device?") await self.enc_ctx.coap_ctx.shutdown() self.enc_ctx = None + try: + local_address = self._get_local_ipv6_addr((self.host, self.port)) + except Exception as e: + logger.warning( + f"Unable to get local IPv6 address to connect to {self.address}: {e}" + ) + raise AccessoryDisconnectedError("Unable to get local IPv6 address") from e + root = resource.Site() - coap_client = await Context.create_server_context(root, bind=("::", 0)) + coap_client = await Context.create_server_context(root, bind=(local_address, 0)) uri = "coap://%s/2" % (self.address) logger.debug(f"Pair verify uri={uri}") diff --git a/aiohomekit/controller/coap/pairing.py b/aiohomekit/controller/coap/pairing.py index 017587ba..fdd36f7e 100644 --- a/aiohomekit/controller/coap/pairing.py +++ b/aiohomekit/controller/coap/pairing.py @@ -53,6 +53,8 @@ def _async_endpoint_changed(self) -> None: self.connection.address = ( f"[{self.description.address}]:{self.description.port}" ) + self.connection.host = self.description.address + self.connection.port = self.description.port async_create_task(self.connection.reconnect_soon()) @property