11"""Elro Connects cloud authentication."""
22
33from __future__ import annotations
4+ import asyncio
45from datetime import datetime
5-
66from json import dumps
77from dataclasses import dataclass
88from typing import TypedDict
99
10+
11+ import socket
12+ import json
13+ import logging
1014import aiohttp
1115
16+
1217CLIENT_TYPE = "APP"
1318PID = "01288154146"
14- URL_LOGIN = "https://uaa-openapi.hekreu.me/login"
15- URL_DEVICE_LIST = "https://user-openapi.hekreu.me/device"
19+ BASE_UAA_URL = "https://uaa-openapi."
20+ BASE_USER_URL = "https://user-openapi."
21+ DEFAULT_DOMAIN = "hekr.me"
22+
23+ _LOGGER = logging .getLogger (__name__ )
1624
1725
1826@dataclass
@@ -48,6 +56,7 @@ class ElroConnectsSession:
4856 def __init__ (self ) -> None :
4957 """Initialize."""
5058 self ._session_cache = None
59+ self ._domain = None
5160
5261 async def async_login (
5362 self , username : str , password : str
@@ -62,9 +71,13 @@ async def async_login(
6271 headers = {
6372 "User-Agent" : "lib-elro-connects" ,
6473 }
65-
74+ domain = await self . _async_get_domain ()
6675 async with aiohttp .ClientSession (json_serialize = dumps ) as session :
67- async with session .post (URL_LOGIN , json = body , headers = headers ) as resp :
76+ async with session .post (
77+ BASE_UAA_URL + domain + "/login" ,
78+ json = body ,
79+ headers = headers ,
80+ ) as resp :
6881 response = await resp .json ()
6982
7083 response ["last_login" ] = datetime .now ()
@@ -76,6 +89,48 @@ def session(self) -> ElroConnectsCloudSessionCache | None:
7689 """Return the current session."""
7790 return self ._session_cache
7891
92+ async def _async_get_domain (self ) -> str :
93+ """Return the API main domain name address."""
94+
95+ def _get_domain () -> str | None :
96+ # domain has not been determined
97+ sckt = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
98+ host = "info.hekr.me"
99+ port = 91
100+ sckt .connect ((host , port ))
101+ message = '{"action":"getAppDomain"}\n '
102+ sckt .send (message .encode ())
103+ msg = sckt .recv (1024 )
104+
105+ try :
106+ parsed_data = json .loads (msg )
107+ _LOGGER .debug (
108+ "%s result code: %s description: %s" ,
109+ host ,
110+ parsed_data ["code" ],
111+ parsed_data ["desc" ],
112+ )
113+
114+ if "dcInfo" in parsed_data :
115+ self ._domain = parsed_data ["dcInfo" ]["domain" ]
116+ except json .decoder .JSONDecodeError :
117+ return DEFAULT_DOMAIN
118+
119+ if not self ._domain .startswith ("hekr" ): # default
120+ self ._domain = None
121+ return DEFAULT_DOMAIN
122+
123+ return self ._domain
124+
125+ # Only run once
126+ if self ._domain :
127+ return self ._domain
128+
129+ loop = asyncio .get_event_loop ()
130+ if domain := await loop .run_in_executor (None , _get_domain ):
131+ self ._domain = domain
132+ return domain
133+
79134 async def async_get_connectors (self ) -> list [ElroConnectsConnector ]:
80135 """Return as list of registered connectors."""
81136
@@ -88,8 +143,12 @@ async def async_get_connectors(self) -> list[ElroConnectsConnector]:
88143 "Authorization" : f"Bearer { self ._session_cache ['access_token' ]} " ,
89144 }
90145
146+ domain = await self ._async_get_domain ()
91147 async with aiohttp .ClientSession (json_serialize = dumps ) as session :
92- async with session .get (URL_DEVICE_LIST , headers = headers ) as resp :
148+ async with session .get (
149+ BASE_USER_URL + domain + "/device" ,
150+ headers = headers ,
151+ ) as resp :
93152 response = await resp .json ()
94153 connector_list = [
95154 ElroConnectsConnector (
0 commit comments