diff --git a/pyhiveapi/apyhiveapi/api/hive_api.py b/pyhiveapi/apyhiveapi/api/hive_api.py index c81e282..2d34f01 100644 --- a/pyhiveapi/apyhiveapi/api/hive_api.py +++ b/pyhiveapi/apyhiveapi/api/hive_api.py @@ -15,21 +15,22 @@ class HiveApi: def __init__(self, hiveSession=None, websession=None, token=None): """Hive API initialisation.""" self.cameraBaseUrl = "prod.hcam.bgchtest.info" + self.baseUrl = "https://beekeeper.hivehome.com/1.0" + self.urls = { "properties": "https://sso.hivehome.com/", "login": "https://beekeeper.hivehome.com/1.0/cognito/login", "refresh": "https://beekeeper.hivehome.com/1.0/cognito/refresh-token", "long_lived": "https://api.prod.bgchprod.info/omnia/accessTokens", - "base": "https://beekeeper-uk.hivehome.com/1.0", "weather": "https://weather.prod.bgchprod.info/weather", "holiday_mode": "/holiday-mode", - "all": "/nodes/all?products=true&devices=true&actions=true", - "alarm": "/security-lite?homeId=", + "all": f"{self.baseUrl}/nodes/all", + "alarm": f"{self.baseUrl}/security-lite", "cameraImages": f"https://event-history-service.{self.cameraBaseUrl}/v1/events/cameras?latest=true&cameraId={{0}}", "cameraRecordings": f"https://event-history-service.{self.cameraBaseUrl}/v1/playlist/cameras/{{0}}/events/{{1}}.m3u8", - "devices": "/devices", - "products": "/products", - "actions": "/actions", + "devices": f"{self.baseUrl}/devices", + "products": f"{self.baseUrl}/products", + "actions": f"{self.baseUrl}/actions", "nodes": "/nodes/{0}/{1}", } self.timeout = 10 @@ -40,7 +41,28 @@ def __init__(self, hiveSession=None, websession=None, token=None): self.session = hiveSession self.token = token - def request(self, type, url, jsc=None, camera=False): + self.homeID = None + if self.session is not None: + self.homeID = self.session.config.homeID + + def getParams(self, products=False, devices=False, actions=False): + """Get parameters.""" + params = { + "products": products, + "devices": devices, + "actions": actions, + } + if self.homeID is not None: + params.update({"homeId": self.homeID}) + return params + + def getHomeIdParam(self): + """Get homeId parameter if set.""" + if self.homeID is not None: + return {"homeId": self.homeID} + return {} + + def request(self, type, url, jsc=None, camera=False, params={}): """Make API request.""" if self.session is not None: if camera: @@ -73,11 +95,19 @@ def request(self, type, url, jsc=None, camera=False): if type == "GET": return requests.get( - url=url, headers=self.headers, data=jsc, timeout=self.timeout + url=url, + headers=self.headers, + data=jsc, + timeout=self.timeout, + params=params, ) if type == "POST": return requests.post( - url=url, headers=self.headers, data=jsc, timeout=self.timeout + url=url, + headers=self.headers, + data=jsc, + timeout=self.timeout, + params=params, ) def refreshTokens(self, tokens={}): @@ -97,8 +127,8 @@ def refreshTokens(self, tokens={}): data = json.loads(info.text) if "token" in data and self.session: self.session.updateTokens(data) - self.urls.update({"base": data["platform"]["endpoint"]}) - self.urls.update({"camera": data["platform"]["cameraPlatform"]}) + self.baseUrl = info["platform"]["endpoint"] + self.cameraBaseUrl = info["platform"]["cameraPlatform"] self.json_return.update({"original": info.status_code}) self.json_return.update({"parsed": info.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -132,9 +162,12 @@ def getLoginInfo(self): def getAll(self): """Build and query all endpoint.""" json_return = {} - url = self.urls["base"] + self.urls["all"] + url = self.urls["all"] + params = self.getParams( + products=True, devices=True, actions=True + ) try: - info = self.request("GET", url) + info = self.request("GET", url, params=params) json_return.update({"original": info.status_code}) json_return.update({"parsed": info.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -142,13 +175,34 @@ def getAll(self): return json_return + def getHomes(self): + """Build and query all endpoint.""" + json_return = {} + url = self.urls["all"] + params = self.getParams() + try: + info = self.request("GET", url, params=params) + all = info.json() + json_return.update({"original": info.status_code}) + json_return.update({"parsed": all["homes"]}) + except (OSError, RuntimeError, ZeroDivisionError): + self.error() + + return json_return + def getAlarm(self, homeID=None): """Build and query alarm endpoint.""" if self.session is not None: homeID = self.session.config.homeID - url = self.urls["base"] + self.urls["alarm"] + homeID + url = self.urls["alarm"] + params = {} + if homeID: + params = {"homeID": homeID} + if self.homeID: + # ignore homeID if set in session + params = self.getHomeIdParam() try: - info = self.request("GET", url) + info = self.request("GET", url, params=params) self.json_return.update({"original": info.status_code}) self.json_return.update({"parsed": info.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -186,9 +240,10 @@ def getCameraRecording(self, device=None, eventId=None): def getDevices(self): """Call the get devices endpoint.""" - url = self.urls["base"] + self.urls["devices"] + url = self.urls["devices"] + params = self.getParams(devices=True) try: - response = self.request("GET", url) + response = self.request("GET", url, params=params) self.json_return.update({"original": response.status_code}) self.json_return.update({"parsed": response.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -198,9 +253,10 @@ def getDevices(self): def getProducts(self): """Call the get products endpoint.""" - url = self.urls["base"] + self.urls["products"] + url = self.urls["products"] + params = self.getParams(products=True) try: - response = self.request("GET", url) + response = self.request("GET", url, params=params) self.json_return.update({"original": response.status_code}) self.json_return.update({"parsed": response.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -210,11 +266,13 @@ def getProducts(self): def getActions(self): """Call the get actions endpoint.""" - url = self.urls["base"] + self.urls["actions"] + url = self.urls["all"] + params = self.getHomeIdParam() try: - response = self.request("GET", url) + response = self.request("GET", url, params=params) + all = response.json() self.json_return.update({"original": response.status_code}) - self.json_return.update({"parsed": response.json()}) + self.json_return.update({"parsed": all["actions"]}) except (OSError, RuntimeError, ZeroDivisionError): self.error() @@ -256,6 +314,10 @@ def getWeather(self, weather_url): return self.json_return + def setHome(self, homeID): + """Set the homeID.""" + self.homeID = homeID + def setState(self, n_type, n_id, **kwargs): """Set the state of a Device.""" jsc = ( diff --git a/pyhiveapi/apyhiveapi/api/hive_async_api.py b/pyhiveapi/apyhiveapi/api/hive_async_api.py index c8018a1..baf3ded 100644 --- a/pyhiveapi/apyhiveapi/api/hive_async_api.py +++ b/pyhiveapi/apyhiveapi/api/hive_async_api.py @@ -27,8 +27,9 @@ def __init__(self, hiveSession=None, websession: Optional[ClientSession] = None) "login": f"{self.baseUrl}/cognito/login", "refresh": f"{self.baseUrl}/cognito/refresh-token", "holiday_mode": f"{self.baseUrl}/holiday-mode", - "all": f"{self.baseUrl}/nodes/all?products=true&devices=true&actions=true", - "alarm": f"{self.baseUrl}/security-lite?homeId=", + "all": f"{self.baseUrl}/nodes/all", + "homes": f"{self.baseUrl}/nodes/all", + "alarm": f"{self.baseUrl}/security-lite", "cameraImages": f"https://event-history-service.{self.cameraBaseUrl}/v1/events/cameras?latest=true&cameraId={{0}}", "cameraRecordings": f"https://event-history-service.{self.cameraBaseUrl}/v1/playlist/cameras/{{0}}/events/{{1}}.m3u8", "devices": f"{self.baseUrl}/devices", @@ -44,10 +45,30 @@ def __init__(self, hiveSession=None, websession: Optional[ClientSession] = None) "parsed": "No response to Hive API request", } self.session = hiveSession + self.homeID = None + if self.session is not None: + self.homeID = self.session.config.homeID self.websession = ClientSession() if websession is None else websession + def getParams(self, products=False, devices=False, actions=False): + """Get parameters.""" + params = { + "products": 'true' if products else 'false', + "devices": 'true' if devices else 'false', + "actions": 'true' if actions else 'false', + } + if self.homeID: + params.update({"homeId": self.homeID}) + return params + + def getHomeIdParam(self): + """Get homeId parameter if set.""" + if self.homeID is not None: + return {"homeId": self.homeID} + return {} + async def request( - self, method: str, url: str, camera: bool = False, **kwargs + self, method: str, url: str, camera: bool = False, params={}, **kwargs ) -> ClientResponse: """Make a request.""" data = kwargs.get("data", None) @@ -73,7 +94,7 @@ async def request( raise NoApiToken async with self.websession.request( - method, url, headers=headers, data=data + method, url, headers=headers, data=data, params=params ) as resp: await resp.text() if operator.contains(str(resp.status), "20"): @@ -142,8 +163,11 @@ async def getAll(self): """Build and query all endpoint.""" json_return = {} url = self.urls["all"] + params = self.getParams( + products=True, devices=True, actions=True + ) try: - resp = await self.request("get", url) + resp = await self.request("get", url, params=params) json_return.update({"original": resp.status}) json_return.update({"parsed": await resp.json(content_type=None)}) except (OSError, RuntimeError, ZeroDivisionError): @@ -151,12 +175,28 @@ async def getAll(self): return json_return + async def getHomes(self): + """Build and query all endpoint for homes.""" + json_return = {} + url = self.urls["all"] + params = self.getParams() + try: + resp = await self.request("get", url, params=params) + all = await resp.json(content_type=None) + json_return.update({"original": resp.status}) + json_return.update({"parsed": all["homes"]}) + except (OSError, RuntimeError, ZeroDivisionError): + await self.error() + + return json_return + async def getAlarm(self): """Build and query alarm endpoint.""" json_return = {} - url = self.urls["alarm"] + self.session.config.homeID + url = self.urls["alarm"] + params = self.getHomeIdParam() try: - resp = await self.request("get", url) + resp = await self.request("get", url, params=params) json_return.update({"original": resp.status}) json_return.update({"parsed": await resp.json(content_type=None)}) except (OSError, RuntimeError, ZeroDivisionError): @@ -197,8 +237,9 @@ async def getDevices(self): """Call the get devices endpoint.""" json_return = {} url = self.urls["devices"] + params = self.getParams(devices=True) try: - resp = await self.request("get", url) + resp = await self.request("get", url, params=params) json_return.update({"original": resp.status}) json_return.update({"parsed": await resp.json(content_type=None)}) except (OSError, RuntimeError, ZeroDivisionError): @@ -210,8 +251,9 @@ async def getProducts(self): """Call the get products endpoint.""" json_return = {} url = self.urls["products"] + params = self.getParams(products=True) try: - resp = await self.request("get", url) + resp = await self.request("get", url, params=params) json_return.update({"original": resp.status}) json_return.update({"parsed": await resp.json(content_type=None)}) except (OSError, RuntimeError, ZeroDivisionError): @@ -222,11 +264,13 @@ async def getProducts(self): async def getActions(self): """Call the get actions endpoint.""" json_return = {} - url = self.urls["actions"] + url = self.urls["all"] + params = self.getParams(actions=True) try: - resp = await self.request("get", url) + resp = await self.request("get", url, params=params) + all = await resp.json(content_type=None) json_return.update({"original": resp.status}) - json_return.update({"parsed": await resp.json(content_type=None)}) + json_return.update({"parsed": all["actions"]}) except (OSError, RuntimeError, ZeroDivisionError): await self.error() @@ -270,6 +314,10 @@ async def getWeather(self, weather_url): return json_return + def setHome(self, homeID): + """Set the home ID.""" + self.homeID = homeID + async def setState(self, n_type, n_id, **kwargs): """Set the state of a Device.""" json_return = {} @@ -306,10 +354,12 @@ async def setAlarm(self, **kwargs): + "}" ) - url = f"{self.urls['alarm']}{self.session.config.homeID}" + url = self.urls["alarm"] + params = self.getHomeIdParam() + try: await self.isFileBeingUsed() - resp = await self.request("post", url, data=jsc) + resp = await self.request("post", url, data=jsc, params=params) json_return["original"] = resp.status json_return["parsed"] = await resp.json(content_type=None) except (FileInUse, OSError, RuntimeError, ConnectionError) as e: diff --git a/pyhiveapi/apyhiveapi/api/hive_auth_async.py b/pyhiveapi/apyhiveapi/api/hive_auth_async.py index d3540af..33df18d 100644 --- a/pyhiveapi/apyhiveapi/api/hive_auth_async.py +++ b/pyhiveapi/apyhiveapi/api/hive_auth_async.py @@ -537,7 +537,7 @@ async def refresh_token(self, token): if self.client is None: await self.async_init() result = None - auth_params = ({"REFRESH_TOKEN": token},) + auth_params = {"REFRESH_TOKEN": token} if self.device_key is not None: auth_params = { "REFRESH_TOKEN": token, diff --git a/pyhiveapi/apyhiveapi/session.py b/pyhiveapi/apyhiveapi/session.py index 89efccb..3499f52 100644 --- a/pyhiveapi/apyhiveapi/session.py +++ b/pyhiveapi/apyhiveapi/session.py @@ -61,7 +61,6 @@ def __init__( username=username, password=password, ) - self.api = API(hiveSession=self, websession=websession) self.helper = HiveHelper(self) self.attr = HiveAttributes(self) self.log = Logger(self) @@ -101,6 +100,8 @@ def __init__( ) self.devices = {} self.deviceList = {} + self.api = API(hiveSession=self, websession=websession) + def openFile(self, file: str): """Open a file. @@ -444,7 +445,8 @@ async def getDevices(self, n_id: str): for aAction in api_resp_p[hiveType]: tmpActions.update({aAction["id"]: aAction}) if hiveType == "homes": - self.config.homeID = api_resp_p[hiveType]["homes"][0]["id"] + if self.config.homeID is None: + self.config.homeID = api_resp_p[hiveType]["homes"][0]["id"] if len(tmpProducts) > 0: self.data.products = copy.deepcopy(tmpProducts) @@ -489,6 +491,9 @@ async def startSession(self, config: dict = {}): if not self.config.file and "tokens" not in config: raise HiveUnknownConfiguration + + if "house" in config: + self.api.setHome(config["house"]) try: await self.getDevices("No_ID")