From 607aa8deeb341911c1a66253017e444fbbe1f38b Mon Sep 17 00:00:00 2001 From: Antoine Weill--Duflos Date: Sat, 16 Nov 2024 14:25:08 -0500 Subject: [PATCH 1/3] added second websocket to get the Challenge ws. --- pyhilo/api.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++- pyhilo/const.py | 2 ++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 761e23f..6c551dd 100755 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -28,6 +28,7 @@ API_REGISTRATION_ENDPOINT, API_REGISTRATION_HEADERS, AUTOMATION_DEVICEHUB_ENDPOINT, + AUTOMATION_CHALLENGE_ENDPOINT, DEFAULT_STATE_FILE, DEFAULT_USER_AGENT, FB_APP_ID, @@ -82,7 +83,8 @@ def __init__( self.session: ClientSession = session self._oauth_session = oauth_session self.websocket: WebsocketClient - self.log_traces = log_traces + self.websocket2: WebsocketClient + self.log_traces = False self._get_device_callbacks: list[Callable[..., Any]] = [] @classmethod @@ -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,11 +362,18 @@ 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() await self.get_websocket_params() + async def post_devicehub_negociate(self) -> tuple[str, str]: LOG.debug("Getting websocket url") url = f"{AUTOMATION_DEVICEHUB_ENDPOINT}/negotiate" @@ -378,6 +391,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) @@ -405,6 +440,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()}" From 16e772351d2d64815f9bfb810dcc8f16948dda09 Mon Sep 17 00:00:00 2001 From: Antoine Weill--Duflos Date: Sat, 16 Nov 2024 14:26:43 -0500 Subject: [PATCH 2/3] reverted a typo when removing the forced logs. --- pyhilo/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 6c551dd..5b1d18b 100755 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -84,7 +84,7 @@ def __init__( self._oauth_session = oauth_session self.websocket: WebsocketClient self.websocket2: WebsocketClient - self.log_traces = False + self.log_traces = log_traces self._get_device_callbacks: list[Callable[..., Any]] = [] @classmethod From f494e9e08c4821924c03cc06df9eae7cf2b396c6 Mon Sep 17 00:00:00 2001 From: "Ian C." <108159253+ic-dev21@users.noreply.github.com> Date: Sun, 17 Nov 2024 09:38:21 -0500 Subject: [PATCH 3/3] Linting --- pyhilo/api.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyhilo/api.py b/pyhilo/api.py index 5b1d18b..da01e16 100755 --- a/pyhilo/api.py +++ b/pyhilo/api.py @@ -27,8 +27,8 @@ API_NOTIFICATIONS_ENDPOINT, API_REGISTRATION_ENDPOINT, API_REGISTRATION_HEADERS, - AUTOMATION_DEVICEHUB_ENDPOINT, AUTOMATION_CHALLENGE_ENDPOINT, + AUTOMATION_DEVICEHUB_ENDPOINT, DEFAULT_STATE_FILE, DEFAULT_USER_AGENT, FB_APP_ID, @@ -368,12 +368,10 @@ async def _async_post_init(self) -> None: 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() await self.get_websocket_params() - async def post_devicehub_negociate(self) -> tuple[str, str]: LOG.debug("Getting websocket url") url = f"{AUTOMATION_DEVICEHUB_ENDPOINT}/negotiate" @@ -391,7 +389,7 @@ 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()