diff --git a/pyhilo/api.py b/pyhilo/api.py index 761e23f..da01e16 100755 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -27,6 +27,7 @@ API_NOTIFICATIONS_ENDPOINT, API_REGISTRATION_ENDPOINT, API_REGISTRATION_HEADERS, + AUTOMATION_CHALLENGE_ENDPOINT, AUTOMATION_DEVICEHUB_ENDPOINT, DEFAULT_STATE_FILE, DEFAULT_USER_AGENT, @@ -82,6 +83,7 @@ def __init__( self.session: ClientSession = session self._oauth_session = oauth_session self.websocket: WebsocketClient + self.websocket2: WebsocketClient self.log_traces = log_traces self._get_device_callbacks: list[Callable[..., Any]] = [] @@ -227,6 +229,10 @@ async def _async_request( kwargs["headers"]["authorization"] = f"Bearer {access_token}" kwargs["headers"]["Host"] = host + if endpoint.startswith(AUTOMATION_CHALLENGE_ENDPOINT): + # remove Ocp-Apim-Subscription-Key header to avoid 401 error + kwargs["headers"].pop("Ocp-Apim-Subscription-Key", None) + data: dict[str, Any] = {} url = parse.urljoin(f"https://{host}", endpoint) if self.log_traces: @@ -356,6 +362,11 @@ async def _async_post_init(self) -> None: await self._get_device_token() await self.refresh_ws_token() self.websocket = WebsocketClient(self) + LOG.debug("Websocket2 postinit") + await self._get_fid() + await self._get_device_token() + await self.refresh_ws2_token() + self.websocket2 = WebsocketClient(self) async def refresh_ws_token(self) -> None: (self.ws_url, self.ws_token) = await self.post_devicehub_negociate() @@ -379,6 +390,28 @@ async def post_devicehub_negociate(self) -> tuple[str, str]: ) return (ws_url, ws_token) + async def refresh_ws2_token(self) -> None: + (self.ws2_url, self.ws2_token) = await self.post_challengehub_negociate() + await self.get_websocket2_params() + + async def post_challengehub_negociate(self) -> tuple[str, str]: + LOG.debug("Getting websocket2 url") + url = f"{AUTOMATION_CHALLENGE_ENDPOINT}/negotiate" + LOG.debug(f"challengehub URL is {url}") + resp = await self.async_request("post", url) + ws_url = resp.get("url") + ws_token = resp.get("accessToken") + LOG.debug("Calling set_state challengehub_negotiate") + await set_state( + self._state_yaml, + "websocket2", + { + "url": ws_url, + "token": ws_token, + }, + ) + return (ws_url, ws_token) + async def get_websocket_params(self) -> None: uri = parse.urlparse(self.ws_url) LOG.debug("Getting websocket params") @@ -405,6 +438,32 @@ async def get_websocket_params(self) -> None: LOG.debug("Calling set_state websocket_params") await set_state(self._state_yaml, "websocket", websocket_dict) + async def get_websocket2_params(self) -> None: + uri = parse.urlparse(self.ws2_url) + LOG.debug("Getting websocket2 params") + LOG.debug(f"Getting uri {uri}") + resp: dict[str, Any] = await self.async_request( + "post", + f"{uri.path}negotiate?{uri.query}", + host=uri.netloc, + headers={ + "authorization": f"Bearer {self.ws2_token}", + }, + ) + conn_id: str = resp.get("connectionId", "") + self.full_ws2_url = f"{self.ws2_url}&id={conn_id}&access_token={self.ws2_token}" + LOG.debug(f"Getting full ws2 URL {self.full_ws2_url}") + transport_dict: list[WebsocketTransportsDict] = resp.get( + "availableTransports", [] + ) + websocket_dict: WebsocketDict = { + "connection_id": conn_id, + "available_transports": transport_dict, + "full_ws_url": self.full_ws2_url, + } + LOG.debug("Calling set_state websocket2_params") + await set_state(self._state_yaml, "websocket2", websocket_dict) + async def fb_install(self, fb_id: str) -> None: LOG.debug("Posting firebase install") body = { diff --git a/pyhilo/const.py b/pyhilo/const.py index 6441f6d..a53edb1 100644 --- a/pyhilo/const.py +++ b/pyhilo/const.py @@ -42,6 +42,8 @@ # Automation server constant AUTOMATION_DEVICEHUB_ENDPOINT: Final = "/DeviceHub" +AUTOMATION_CHALLENGE_ENDPOINT: Final = "/ChallengeHub" + # Request constants DEFAULT_USER_AGENT: Final = f"PyHilo/{PYHILO_VERSION} HomeAssistant/{homeassistant.core.__version__} aiohttp/{aiohttp.__version__} Python/{platform.python_version()}"