diff --git a/src/action.py b/src/action.py index abfcd2b..81acbf3 100644 --- a/src/action.py +++ b/src/action.py @@ -34,21 +34,21 @@ async def getAction(self, device: dict): """ dev_data = {} - if device["hiveID"] in self.data["action"]: + if device.hive_id in self.data["action"]: dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], - "haType": device["haType"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, + "haType": device.ha_type, "status": {"state": await self.getState(device)}, "power_usage": None, "deviceData": {}, - "custom": device.get("custom", None), + "custom": getattr(device, "custom", None), } - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: exists = self.session.data.actions.get("hiveID", False) if exists is False: @@ -67,7 +67,7 @@ async def getState(self, device: dict): final = None try: - data = self.session.data.actions[device["hiveID"]] + data = self.session.data.actions[device.hive_id] final = data["enabled"] except KeyError as e: _LOGGER.error(e) @@ -87,16 +87,16 @@ async def setStatusOn(self, device: dict): final = False - if device["hiveID"] in self.session.data.actions: + if device.hive_id in self.session.data.actions: _LOGGER.debug("Enabling action %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.actions[device["hiveID"]] + data = self.session.data.actions[device.hive_id] data.update({"enabled": True}) send = json.dumps(data) - resp = await self.session.api.setAction(device["hiveID"], send) + resp = await self.session.api.setAction(device.hive_id, send) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -113,15 +113,15 @@ async def setStatusOff(self, device: dict): final = False - if device["hiveID"] in self.session.data.actions: + if device.hive_id in self.session.data.actions: _LOGGER.debug("Disabling action %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.actions[device["hiveID"]] + data = self.session.data.actions[device.hive_id] data.update({"enabled": False}) send = json.dumps(data) - resp = await self.session.api.setAction(device["hiveID"], send) + resp = await self.session.api.setAction(device.hive_id, send) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final diff --git a/src/alarm.py b/src/alarm.py deleted file mode 100644 index 2108962..0000000 --- a/src/alarm.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Hive Alarm Module.""" - -# pylint: skip-file -import logging - -_LOGGER = logging.getLogger(__name__) - - -class HiveHomeShield: - """Hive homeshield alarm. - - Returns: - object: Hive homeshield - """ - - alarmType = "Alarm" - - async def getMode(self): - """Get current mode of the alarm. - - Returns: - str: Mode if the alarm [armed_home, armed_away, armed_night] - """ - state = None - - try: - data = self.session.data.alarm - state = data["mode"] - except KeyError as e: - _LOGGER.error(e) - - return state - - async def getState(self, device: dict): - """Get the alarm triggered state. - - Returns: - boolean: True/False if alarm is triggered. - """ - state = None - - try: - data = self.session.data.devices[device["hiveID"]] - state = data["state"]["alarmActive"] - except KeyError as e: - _LOGGER.error(e) - - return state - - async def setMode(self, device: dict, mode: str): - """Set the alarm mode. - - Args: - device (dict): Alarm device. - - Returns: - boolean: True/False if successful. - """ - final = False - - if ( - device["hiveID"] in self.session.data.devices - and device["deviceData"]["online"] - ): - _LOGGER.debug("Setting alarm mode to %s.", mode) - await self.session.hiveRefreshTokens() - resp = await self.session.api.setAlarm(mode=mode) - if resp["original"] == 200: - final = True - await self.session.getAlarm() - - return final - - -class Alarm(HiveHomeShield): - """Home assistant alarm. - - Args: - HiveHomeShield (object): Class object. - """ - - def __init__(self, session: object = None): - """Initialise alarm. - - Args: - session (object, optional): Used to interact with the hive account. Defaults to None. - """ - self.session = session - - async def getAlarm(self, device: dict): - """Get alarm data. - - Args: - device (dict): Device to update. - - Returns: - dict: Updated device. - """ - device["deviceData"].update( - {"online": await self.session.attr.onlineOffline(device["device_id"])} - ) - dev_data = {} - - if device["deviceData"]["online"]: - self.session.helper.deviceRecovered(device["device_id"]) - _LOGGER.debug("Updating alarm data for %s.", device["haName"]) - data = self.session.data.devices[device["device_id"]] - dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], - "haType": device["haType"], - "device_id": device["device_id"], - "device_name": device["device_name"], - "status": { - "state": await self.getState(device), - "mode": await self.getMode(), - }, - "deviceData": data.get("props", None), - "parentDevice": data.get("parent", None), - "custom": device.get("custom", None), - "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] - ), - } - - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] - else: - await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] - ) - device.setdefault("status", {"state": None, "mode": None}) - return device diff --git a/src/api/hive_api.py b/src/api/hive_api.py index a2f0c44..6971055 100644 --- a/src/api/hive_api.py +++ b/src/api/hive_api.py @@ -15,7 +15,6 @@ class HiveApi: def __init__(self, hiveSession=None, websession=None, token=None): """Hive API initialisation.""" - self.cameraBaseUrl = "prod.hcam.bgchtest.info" self.urls = { "properties": "https://sso.hivehome.com/", "login": "https://beekeeper.hivehome.com/1.0/cognito/login", @@ -25,9 +24,6 @@ def __init__(self, hiveSession=None, websession=None, token=None): "weather": "https://weather.prod.bgchprod.info/weather", "holiday_mode": "/holiday-mode", "all": "/nodes/all?products=true&devices=true&actions=true", - "alarm": "/security-lite?homeId=", - "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", @@ -41,36 +37,20 @@ def __init__(self, hiveSession=None, websession=None, token=None): self.session = hiveSession self.token = token - def request(self, type, url, jsc=None, camera=False): + def request(self, type, url, jsc=None): """Make API request.""" if self.session is not None: - if camera: - self.headers = { - "content-type": "application/json", - "Accept": "*/*", - "Authorization": f"Bearer {self.session.tokens.tokenData['token']}", - "x-jwt-token": self.session.tokens.tokenData["token"], - } - else: - self.headers = { - "content-type": "application/json", - "Accept": "*/*", - "authorization": self.session.tokens.tokenData["token"], - } + self.headers = { + "content-type": "application/json", + "Accept": "*/*", + "authorization": self.session.tokens.token_data["token"], + } else: - if camera: - self.headers = { - "content-type": "application/json", - "Accept": "*/*", - "Authorization": f"Bearer {self.token}", - "x-jwt-token": self.token, - } - else: - self.headers = { - "content-type": "application/json", - "Accept": "*/*", - "authorization": self.token, - } + self.headers = { + "content-type": "application/json", + "Accept": "*/*", + "authorization": self.token, + } if type == "GET": return requests.get( @@ -99,7 +79,6 @@ def refreshTokens(self, tokens={}): 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.json_return.update({"original": info.status_code}) self.json_return.update({"parsed": info.json()}) except (OSError, RuntimeError, ZeroDivisionError): @@ -143,48 +122,6 @@ def getAll(self): 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 - try: - info = self.request("GET", url) - self.json_return.update({"original": info.status_code}) - self.json_return.update({"parsed": info.json()}) - except (OSError, RuntimeError, ZeroDivisionError): - self.error() - - return self.json_return - - def getCameraImage(self, device=None, accessToken=None): - """Build and query camera endpoint.""" - json_return = {} - url = self.urls["cameraImages"].format(device["props"]["hardwareIdentifier"]) - try: - info = self.request("GET", url, camera=True) - json_return.update({"original": info.status_code}) - json_return.update({"parsed": info.json()}) - except (OSError, RuntimeError, ZeroDivisionError): - self.error() - - return json_return - - def getCameraRecording(self, device=None, eventId=None): - """Build and query camera endpoint.""" - json_return = {} - url = self.urls["cameraRecordings"].format( - device["props"]["hardwareIdentifier"], eventId - ) - try: - info = self.request("GET", url, camera=True) - json_return.update({"original": info.status_code}) - json_return.update({"parsed": info.text.split("\n")[3]}) - except (OSError, RuntimeError, ZeroDivisionError): - self.error() - - return json_return - def getDevices(self): """Call the get devices endpoint.""" url = self.urls["base"] + self.urls["devices"] diff --git a/src/api/hive_async_api.py b/src/api/hive_async_api.py index 8a29a2e..979cd32 100644 --- a/src/api/hive_async_api.py +++ b/src/api/hive_async_api.py @@ -26,16 +26,12 @@ class HiveApiAsync: def __init__(self, hiveSession=None, websession: Optional[ClientSession] = None): """Hive API initialisation.""" self.baseUrl = "https://beekeeper.hivehome.com/1.0" - self.cameraBaseUrl = "prod.hcam.bgchtest.info" self.urls = { "properties": "https://sso.hivehome.com/", "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=", - "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", "products": f"{self.baseUrl}/products", "actions": f"{self.baseUrl}/actions", @@ -51,9 +47,7 @@ def __init__(self, hiveSession=None, websession: Optional[ClientSession] = None) self.session = hiveSession self.websession = ClientSession() if websession is None else websession - async def request( - self, method: str, url: str, camera: bool = False, **kwargs - ) -> ClientResponse: + async def request(self, method: str, url: str, **kwargs) -> ClientResponse: """Make a request.""" _LOGGER.debug("API %s request to %s", method.upper(), url) data = kwargs.get("data", None) @@ -64,13 +58,7 @@ async def request( "User-Agent": "Hive/12.04.0 iOS/18.3.1 Apple", } try: - if camera: - headers["Authorization"] = ( - f"Bearer {self.session.tokens.tokenData['token']}" - ) - headers["x-jwt-token"] = self.session.tokens.tokenData["token"] - else: - headers["Authorization"] = self.session.tokens.tokenData["token"] + headers["Authorization"] = self.session.tokens.token_data["token"] except KeyError: if "sso" in url: pass @@ -158,7 +146,6 @@ async def refreshTokens(self): if "token" in info: await self.session.updateTokens(info) self.baseUrl = info["platform"]["endpoint"] - self.cameraBaseUrl = info["platform"]["cameraPlatform"] return True except (ConnectionError, OSError, RuntimeError, ZeroDivisionError): await self.error() @@ -182,48 +169,6 @@ async def getAll(self): return json_return - async def getAlarm(self): - """Build and query alarm endpoint.""" - json_return = {} - url = self.urls["alarm"] + self.session.config.homeID - try: - resp = await self.request("get", url) - json_return.update({"original": resp.status}) - json_return.update({"parsed": await resp.json(content_type=None)}) - except (OSError, RuntimeError, ZeroDivisionError): - await self.error() - - return json_return - - async def getCameraImage(self, device): - """Build and query alarm endpoint.""" - json_return = {} - url = self.urls["cameraImages"].format(device["props"]["hardwareIdentifier"]) - try: - resp = await self.request("get", url, True) - json_return.update({"original": resp.status}) - json_return.update({"parsed": await resp.json(content_type=None)}) - except (OSError, RuntimeError, ZeroDivisionError): - await self.error() - - return json_return - - async def getCameraRecording(self, device, eventId): - """Build and query alarm endpoint.""" - json_return = {} - url = self.urls["cameraRecordings"].format( - device["props"]["hardwareIdentifier"], eventId - ) - try: - resp = await self.request("get", url, True) - recUrl = await resp.text() - json_return.update({"original": resp.status}) - json_return.update({"parsed": recUrl.split("\n")[3]}) - except (OSError, RuntimeError, ZeroDivisionError): - await self.error() - - return json_return - async def getDevices(self): """Call the get devices endpoint.""" json_return = {} @@ -327,32 +272,6 @@ async def setState(self, n_type, n_id, **kwargs): return json_return - async def setAlarm(self, **kwargs): - """Set the state of the alarm.""" - _LOGGER.debug("Setting alarm state: %s", kwargs) - json_return = {} - jsc = ( - "{" - + ",".join( - ('"' + str(i) + '": ' '"' + str(t) + '" ' for i, t in kwargs.items()) - ) - + "}" - ) - - url = f"{self.urls['alarm']}{self.session.config.homeID}" - try: - await self.isFileBeingUsed() - resp = await self.request("post", url, data=jsc) - json_return["original"] = resp.status - json_return["parsed"] = await resp.json(content_type=None) - except (FileInUse, OSError, RuntimeError, ConnectionError) as e: - if e.__class__.__name__ == "FileInUse": - return {"original": "file"} - else: - await self.error() - - return json_return - async def setAction(self, n_id, data): """Set the state of a Action.""" _LOGGER.debug("Setting action %s", n_id) diff --git a/src/api/hive_auth.py b/src/api/hive_auth.py index 9ce7d75..ebe174a 100644 --- a/src/api/hive_auth.py +++ b/src/api/hive_auth.py @@ -11,6 +11,8 @@ import boto3 import botocore +from botocore import UNSIGNED +from botocore.config import Config from ..helper.hive_exceptions import ( HiveApiError, @@ -118,12 +120,12 @@ def __init__( # pylint: disable=too-many-positional-arguments self.__pool_id = self.data.get("UPID") self.__client_id = self.data.get("CLIID") self.__region = self.data.get("REGION").split("_")[0] + # Use unsigned requests for Cognito public client (no AWS credentials needed) + config = Config(signature_version=UNSIGNED) self.client = boto3.client( "cognito-idp", - self.__region, - aws_access_key_id="ACCESS_KEY", - aws_secret_access_key="SECRET_KEY", - aws_session_token="SESSION_TOKEN", + region_name=self.__region, + config=config, ) def generate_random_small_a(self): diff --git a/src/api/hive_auth_async.py b/src/api/hive_auth_async.py index 0bef108..eb3c80d 100644 --- a/src/api/hive_auth_async.py +++ b/src/api/hive_auth_async.py @@ -15,6 +15,8 @@ import boto3 import botocore +from botocore import UNSIGNED +from botocore.config import Config from ..helper.hive_exceptions import ( HiveApiError, @@ -111,15 +113,15 @@ async def async_init(self): self.__pool_id = self.data.get("UPID") self.__client_id = self.data.get("CLIID") self.__region = self.data.get("REGION").split("_")[0] + # Use unsigned requests for Cognito public client (no AWS credentials needed) + config = Config(signature_version=UNSIGNED) self.client = await self.loop.run_in_executor( None, functools.partial( boto3.client, "cognito-idp", - self.__region, - aws_access_key_id="ACCESS_KEY", - aws_secret_access_key="SECRET_KEY", - aws_session_token="SESSION_TOKEN", + region_name=self.__region, + config=config, ), ) @@ -425,9 +427,9 @@ async def login(self): self.device_group_key = result["AuthenticationResult"][ "NewDeviceMetadata" ]["DeviceGroupKey"] - self.device_key = result["AuthenticationResult"][ - "NewDeviceMetadata" - ]["DeviceKey"] + self.device_key = result["AuthenticationResult"]["NewDeviceMetadata"][ + "DeviceKey" + ] _LOGGER.debug("SRP auth challenge completed successfully.") return result diff --git a/src/camera.py b/src/camera.py deleted file mode 100644 index 4b3de6f..0000000 --- a/src/camera.py +++ /dev/null @@ -1,193 +0,0 @@ -"""Hive Camera Module.""" - -# pylint: skip-file -import logging - -_LOGGER = logging.getLogger(__name__) - - -class HiveCamera: - """Hive camera. - - Returns: - object: Hive camera - """ - - cameraType = "Camera" - - async def getCameraTemperature(self, device: dict): - """Get the camera state. - - Returns: - boolean: True/False if camera is on. - """ - state = None - - try: - data = self.session.data.devices[device["hiveID"]] - state = data["props"]["temperature"] - except KeyError as e: - _LOGGER.error(e) - - return state - - async def getCameraState(self, device: dict): - """Get the camera state. - - Returns: - boolean: True/False if camera is on. - """ - state = None - - try: - data = self.session.data.devices[device["hiveID"]] - state = True if data["state"]["mode"] == "ARMED" else False - except KeyError as e: - _LOGGER.error(e) - - return state - - async def getCameraImageURL(self, device: dict): - """Get the camera image url. - - Returns: - str: image url. - """ - state = None - - try: - state = self.session.data.camera[device["hiveID"]]["cameraImage"][ - "thumbnailUrls" - ][0] - except KeyError as e: - _LOGGER.error(e) - - return state - - async def getCameraRecodringURL(self, device: dict): - """Get the camera recording url. - - Returns: - str: image url. - """ - state = None - - try: - state = self.session.data.camera[device["hiveID"]]["cameraRecording"] - except KeyError as e: - _LOGGER.error(e) - - return state - - async def setCameraOn(self, device: dict, mode: str): - """Set the camera state to on. - - Args: - device (dict): Camera device. - - Returns: - boolean: True/False if successful. - """ - final = False - - if ( - device["hiveID"] in self.session.data.devices - and device["deviceData"]["online"] - ): - _LOGGER.debug("Setting camera ON for %s.", device["haName"]) - await self.session.hiveRefreshTokens() - resp = await self.session.api.setState(mode=mode) - if resp["original"] == 200: - final = True - await self.session.getCamera() - - return final - - async def setCameraOff(self, device: dict, mode: str): - """Set the camera state to on. - - Args: - device (dict): Camera device. - - Returns: - boolean: True/False if successful. - """ - final = False - - if ( - device["hiveID"] in self.session.data.devices - and device["deviceData"]["online"] - ): - _LOGGER.debug("Setting camera OFF for %s.", device["haName"]) - await self.session.hiveRefreshTokens() - resp = await self.session.api.setState(mode=mode) - if resp["original"] == 200: - final = True - await self.session.getCamera() - - return final - - -class Camera(HiveCamera): - """Home assistant camera. - - Args: - HiveCamera (object): Class object. - """ - - def __init__(self, session: object = None): - """Initialise camera. - - Args: - session (object, optional): Used to interact with the hive account. Defaults to None. - """ - self.session = session - - async def getCamera(self, device: dict): - """Get camera data. - - Args: - device (dict): Device to update. - - Returns: - dict: Updated device. - """ - device["deviceData"].update( - {"online": await self.session.attr.onlineOffline(device["device_id"])} - ) - dev_data = {} - - if device["deviceData"]["online"]: - self.session.helper.deviceRecovered(device["device_id"]) - _LOGGER.debug("Updating camera data for %s.", device["haName"]) - data = self.session.data.devices[device["device_id"]] - dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], - "haType": device["haType"], - "device_id": device["device_id"], - "device_name": device["device_name"], - "status": { - "temperature": await self.getCameraTemperature(device), - "state": await self.getCameraState(device), - "imageURL": await self.getCameraImageURL(device), - "recordingURL": await self.getCameraRecodringURL(device), - }, - "deviceData": data.get("props", None), - "parentDevice": data.get("parent", None), - "custom": device.get("custom", None), - "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] - ), - } - - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] - else: - await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] - ) - device.setdefault("status", {"state": None}) - return device diff --git a/src/data/alarm.json b/src/data/alarm.json deleted file mode 100644 index 5392dec..0000000 --- a/src/data/alarm.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "mode": "home", - "securitySystemState": "ARMED", - "armingGracePeriod": 60, - "alarmingGracePeriod": 60, - "devices": [ - "keypad-0000-0000-000000000001", - "contact-sensor-0000-0000-000000000001", - "siren-0000-0000-000000000001", - "contact-sensor-0000-0000-000000000002" - ], - "triggers": { - "home": [], - "away": [ - "contact-sensor-0000-0000-000000000001", - "contact-sensor-0000-0000-000000000002" - ], - "asleep": [ - "contact-sensor-0000-0000-000000000002", - "contact-sensor-0000-0000-000000000001" - ] - }, - "actions": { - "away": [ - "keypad-0000-0000-000000000001" - ], - "asleep": [ - "keypad-0000-0000-000000000001" - ], - "sos": [ - "keypad-0000-0000-000000000001" - ] - }, - "monitoringCameras": { - "home": [], - "away": [], - "asleep": [] - }, - "alarmingGraceChirp": { - "away": "ON_GRACE_START", - "asleep": "ON_GRACE_START" - }, - "numberOfTriggersToAlarm": { - "away": 1, - "asleep": 1 - }, - "modeValid": { - "home": true, - "away": true, - "asleep": true - }, - "pinSchedules": {} -} \ No newline at end of file diff --git a/src/data/camera.json b/src/data/camera.json deleted file mode 100644 index 604ffbe..0000000 --- a/src/data/camera.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "cameraImage": { - "parsed": { - "events": [ - { - "thumbnailUrls": [ - "https://test.com/image" - ], - "hasRecording": true - } - ] - } - }, - "camaeraRecording": { - "parsed": "https://test.com/video" - } -} \ No newline at end of file diff --git a/src/data/data.json b/src/data/data.json index 246abf4..b030123 100644 --- a/src/data/data.json +++ b/src/data/data.json @@ -700,267 +700,216 @@ } }, { - "id": "camera-0000-0000-0000-000000000001", - "type": "hivecamera", + "id": "plug-0000-0000-0000-000000000001", + "type": "activeplug", "sortOrder": 0, - "created": 1243477586508, - "lastSeen": 1553324607010, + "created": 1295844392614, + "lastSeen": 1549431134911, + "parent": "parent-0000-0000-0000-000000000001", "props": { "online": true, - "model": "HCI001", - "version": "V0_0_00_093_svn825", - "manufacturer": "Hive", - "hardwareIdentifier": "51385FC1F5024E748A446456449E9601", - "ipAddress": "000.168.0.00", - "macAddress": "00:0A:11:BB:22:CC", - "temperature": "40", - "hardwareVersion": "H4", - "capabilities": [ - "CAMERA_VIDEO", - "CAMERA_DETECTION", - "CAMERA_DEVICE", - "NOTIFICATIONS", - "INFORMATION", - "CHANGE_WIFI", - "RENAME", - "DELETE" - ] + "model": "SLP2b", + "version": "01045700", + "manufacturer": "Computime", + "powerConsumption": 0, + "onSince": 1550266428863, + "inUse": false, + "deviceClass": "UNKNOWN", + "capabilities": ["INFORMATION", "RENAME", "DELETE"] }, "state": { - "name": "Camera 1", - "resolution": "1080P", - "motionDetection": "ALL", - "audioDetection": "ALL", - "ledDot": false, - "ledRing": true, - "soundAlert": true, - "nightVision": "AUTO", - "invertImage": false, - "cameraAudio": true, - "motionSensitivity": "LOW", - "audioSensitivity": "LOW", - "timeZone": "Europe/London", - "notificationScheduleEnabled": false, - "notificationsMode": "DISABLED", - "frameRate": "30", - "cameraZoom": "1X", - "storage": "CLOUD", - "activityZone": "ALL", - "scheduleEnabled": false, - "mode": "ARMED", - "systemNotificationSettings": { - "connectionStatus": true, - "batteryLevel": true, - "overheating": true - }, + "name": "Plug 1", + "status": "OFF", + "mode": "MANUAL", "schedule": { "monday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "tuesday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "wednesday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "thursday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "friday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "saturday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ], "sunday": [ { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, + "start": 480, "value": { - "mode": "ARMED" + "status": "OFF" } }, { - "start": 960, + "start": 1320, "value": { - "mode": "PRIVACY" + "status": "ON" } }, { - "start": 1290, + "start": 1380, "value": { - "mode": "ARMED" + "status": "OFF" } } ] - }, - "notificationSchedule": { + } + } + }, + { + "id": "plug-0000-0000-0000-000000000002", + "type": "activeplug", + "sortOrder": 0, + "created": 1523679665643, + "parent": "parent-0000-0000-0000-000000000001", + "props": { + "online": true, + "model": "SLP2b", + "version": "01035700", + "manufacturer": "Computime", + "powerConsumption": 0, + "onSince": 1553134057795, + "inUse": false, + "deviceClass": "UNKNOWN", + "capabilities": ["INFORMATION", "RENAME", "DELETE"] + }, + "state": { + "name": "Plug 2", + "status": "OFF", + "mode": "MANUAL", + "schedule": { "monday": [ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -968,25 +917,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -994,25 +943,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -1020,25 +969,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -1046,25 +995,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -1072,25 +1021,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ], @@ -1098,25 +1047,25 @@ { "start": 390, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 510, "value": { - "state": "ENABLE" + "status": "OFF" } }, { "start": 960, "value": { - "state": "DISABLE" + "status": "ON" } }, { "start": 1290, "value": { - "state": "ENABLE" + "status": "OFF" } } ] @@ -1124,35 +1073,29 @@ } }, { - "id": "plug-0000-0000-0000-000000000001", + "id": "plug-0000-0000-0000-000000000003", "type": "activeplug", "sortOrder": 0, - "created": 1295844392614, - "lastSeen": 1549431134911, + "created": 1512759057107, + "lastSeen": 1549431076054, "parent": "parent-0000-0000-0000-000000000001", "props": { "online": true, "model": "SLP2b", - "version": "01045700", + "version": "01035700", "manufacturer": "Computime", "powerConsumption": 0, - "onSince": 1550266428863, + "onSince": 1550266371454, "inUse": false, "deviceClass": "UNKNOWN", "capabilities": ["INFORMATION", "RENAME", "DELETE"] }, "state": { - "name": "Plug 1", + "name": "Plug 3", "status": "OFF", "mode": "MANUAL", "schedule": { "monday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1167,12 +1110,6 @@ } ], "tuesday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1187,12 +1124,6 @@ } ], "wednesday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1207,12 +1138,6 @@ } ], "thursday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1227,12 +1152,6 @@ } ], "friday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1247,12 +1166,6 @@ } ], "saturday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1267,12 +1180,6 @@ } ], "sunday": [ - { - "start": 480, - "value": { - "status": "OFF" - } - }, { "start": 1320, "value": { @@ -1290,31 +1197,38 @@ } }, { - "id": "plug-0000-0000-0000-000000000002", - "type": "activeplug", + "id": "light-0000-0000-0000-000000000003", + "type": "warmwhitelight", "sortOrder": 0, - "created": 1523679665643, + "created": 1995324452543, + "lastSeen": 1147535994599, "parent": "parent-0000-0000-0000-000000000001", "props": { "online": true, - "model": "SLP2b", - "version": "01035700", - "manufacturer": "Computime", - "powerConsumption": 0, - "onSince": 1553134057795, - "inUse": false, - "deviceClass": "UNKNOWN", - "capabilities": ["INFORMATION", "RENAME", "DELETE"] + "model": "FWBulb01", + "version": "11480002", + "manufacturer": "Aurora", + "capabilities": [ + "INFORMATION", + "RENAME", + "DELETE", + "GROUPABLE", + "MIMIC_MODE", + "SCHEDULE", + "IDENTIFY_DEVICE" + ] }, "state": { - "name": "Plug 2", + "name": "Light 3", "status": "OFF", + "brightness": 100, "mode": "MANUAL", "schedule": { "monday": [ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1327,6 +1241,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1341,6 +1256,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1353,6 +1269,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1367,6 +1284,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1379,6 +1297,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1393,6 +1312,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1405,6 +1325,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1419,6 +1340,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1431,6 +1353,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1445,6 +1368,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1457,6 +1381,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1471,6 +1396,7 @@ { "start": 390, "value": { + "brightness": 100, "status": "ON" } }, @@ -1483,6 +1409,7 @@ { "start": 960, "value": { + "brightness": 100, "status": "ON" } }, @@ -1497,135 +1424,11 @@ } }, { - "id": "plug-0000-0000-0000-000000000003", - "type": "activeplug", - "sortOrder": 0, - "created": 1512759057107, - "lastSeen": 1549431076054, - "parent": "parent-0000-0000-0000-000000000001", - "props": { - "online": true, - "model": "SLP2b", - "version": "01035700", - "manufacturer": "Computime", - "powerConsumption": 0, - "onSince": 1550266371454, - "inUse": false, - "deviceClass": "UNKNOWN", - "capabilities": ["INFORMATION", "RENAME", "DELETE"] - }, - "state": { - "name": "Plug 3", - "status": "OFF", - "mode": "MANUAL", - "schedule": { - "monday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "tuesday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "wednesday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "thursday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "friday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "saturday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ], - "sunday": [ - { - "start": 1320, - "value": { - "status": "ON" - } - }, - { - "start": 1380, - "value": { - "status": "OFF" - } - } - ] - } - } - }, - { - "id": "light-0000-0000-0000-000000000003", + "id": "light-0000-0000-0000-000000000004", "type": "warmwhitelight", "sortOrder": 0, - "created": 1995324452543, - "lastSeen": 1147535994599, + "created": 1495819606370, + "lastSeen": 1549742852622, "parent": "parent-0000-0000-0000-000000000001", "props": { "online": true, @@ -1643,7 +1446,7 @@ ] }, "state": { - "name": "Light 3", + "name": "Light 4", "status": "OFF", "brightness": 100, "mode": "MANUAL", @@ -1848,11 +1651,11 @@ } }, { - "id": "light-0000-0000-0000-000000000004", + "id": "light-0000-0000-0000-000000000002", "type": "warmwhitelight", "sortOrder": 0, - "created": 1495819606370, - "lastSeen": 1549742852622, + "created": 1470158558350, + "lastSeen": 1543686750227, "parent": "parent-0000-0000-0000-000000000001", "props": { "online": true, @@ -1870,34 +1673,21 @@ ] }, "state": { - "name": "Light 4", - "status": "OFF", + "name": "Light 2", + "status": "ON", "brightness": 100, "mode": "MANUAL", "schedule": { "monday": [ { - "start": 390, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 510, - "value": { - "status": "OFF" - } - }, - { - "start": 960, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 1290, + "start": 1350, "value": { "status": "OFF" } @@ -1905,27 +1695,14 @@ ], "tuesday": [ { - "start": 390, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 510, - "value": { - "status": "OFF" - } - }, - { - "start": 960, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 1290, + "start": 1350, "value": { "status": "OFF" } @@ -1933,27 +1710,14 @@ ], "wednesday": [ { - "start": 390, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 510, - "value": { - "status": "OFF" - } - }, - { - "start": 960, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 1290, + "start": 1350, "value": { "status": "OFF" } @@ -1961,247 +1725,59 @@ ], "thursday": [ { - "start": 390, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 510, + "start": 1350, "value": { "status": "OFF" } - }, + } + ], + "friday": [ { - "start": 960, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 1290, + "start": 1350, "value": { "status": "OFF" } } ], - "friday": [ + "saturday": [ { - "start": 390, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 510, + "start": 1350, "value": { "status": "OFF" } - }, + } + ], + "sunday": [ { - "start": 960, + "start": 1095, "value": { - "brightness": 100, + "brightness": 5, "status": "ON" } }, { - "start": 1290, - "value": { - "status": "OFF" - } - } - ], - "saturday": [ - { - "start": 390, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 510, - "value": { - "status": "OFF" - } - }, - { - "start": 960, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 1290, - "value": { - "status": "OFF" - } - } - ], - "sunday": [ - { - "start": 390, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 510, - "value": { - "status": "OFF" - } - }, - { - "start": 960, - "value": { - "brightness": 100, - "status": "ON" - } - }, - { - "start": 1290, - "value": { - "status": "OFF" - } - } - ] - } - } - }, - { - "id": "light-0000-0000-0000-000000000002", - "type": "warmwhitelight", - "sortOrder": 0, - "created": 1470158558350, - "lastSeen": 1543686750227, - "parent": "parent-0000-0000-0000-000000000001", - "props": { - "online": true, - "model": "FWBulb01", - "version": "11480002", - "manufacturer": "Aurora", - "capabilities": [ - "INFORMATION", - "RENAME", - "DELETE", - "GROUPABLE", - "MIMIC_MODE", - "SCHEDULE", - "IDENTIFY_DEVICE" - ] - }, - "state": { - "name": "Light 2", - "status": "ON", - "brightness": 100, - "mode": "MANUAL", - "schedule": { - "monday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "tuesday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "wednesday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "thursday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "friday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "saturday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, - "value": { - "status": "OFF" - } - } - ], - "sunday": [ - { - "start": 1095, - "value": { - "brightness": 5, - "status": "ON" - } - }, - { - "start": 1350, + "start": 1350, "value": { "status": "OFF" } @@ -3551,433 +3127,6 @@ "calibrationStatus": "CALIBRATED" } }, - { - "id": "camera-0000-0000-0000-000000000001", - "type": "hivecamera", - "sortOrder": 0, - "created": 1543677886208, - "lastSeen": 1550320722000, - "props": { - "online": true, - "model": "HCI001", - "version": "V0_0_00_093_svn825", - "manufacturer": "Hive", - "hardwareIdentifier": "51385FC1F5024E748A446456449E9601", - "power": "mains", - "signal": 37, - "battery": 100, - "ipAddress": "000.168.0.00", - "macAddress": "00:0A:11:BB:22:CC", - "temperature": "40", - "hardwareVersion": "H4", - "capabilities": [ - "CAMERA_VIDEO", - "CAMERA_DETECTION", - "CAMERA_DEVICE", - "NOTIFICATIONS", - "INFORMATION", - "CHANGE_WIFI", - "RENAME", - "DELETE" - ] - }, - "state": { - "name": "Camera 1", - "resolution": "1080P", - "motionDetection": "ALL", - "audioDetection": "ALL", - "ledDot": false, - "ledRing": true, - "soundAlert": true, - "nightVision": "AUTO", - "invertImage": false, - "cameraAudio": true, - "motionSensitivity": "LOW", - "audioSensitivity": "LOW", - "timeZone": "Europe/London", - "notificationScheduleEnabled": false, - "notificationsMode": "DISABLED", - "frameRate": "30", - "cameraZoom": "1X", - "storage": "CLOUD", - "activityZone": "ALL", - "scheduleEnabled": false, - "mode": "ARMED", - "systemNotificationSettings": { - "connectionStatus": true, - "batteryLevel": true, - "overheating": true - }, - "schedule": { - "monday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "tuesday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "wednesday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "thursday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "friday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "saturday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ], - "sunday": [ - { - "start": 390, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 510, - "value": { - "mode": "ARMED" - } - }, - { - "start": 960, - "value": { - "mode": "PRIVACY" - } - }, - { - "start": 1290, - "value": { - "mode": "ARMED" - } - } - ] - }, - "notificationSchedule": { - "monday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "tuesday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "wednesday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "thursday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "friday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "saturday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ], - "sunday": [ - { - "start": 390, - "value": { - "state": "DISABLE" - } - }, - { - "start": 510, - "value": { - "state": "ENABLE" - } - }, - { - "start": 960, - "value": { - "state": "DISABLE" - } - }, - { - "start": 1290, - "value": { - "state": "ENABLE" - } - } - ] - } - } - }, { "id": "plug-0000-0000-0000-000000000001", "type": "activeplug", diff --git a/src/heating.py b/src/heating.py index d2bcc67..c51e747 100644 --- a/src/heating.py +++ b/src/heating.py @@ -26,8 +26,8 @@ async def getMinTemperature(self, device: dict): Returns: int: Minimum temperature """ - if device["hiveType"] == "nathermostat": - return self.session.data.products[device["hiveID"]]["props"]["minHeat"] + if device.hive_type == "nathermostat": + return self.session.data.products[device.hive_id]["props"]["minHeat"] return 5 async def getMaxTemperature(self, device: dict): @@ -39,8 +39,8 @@ async def getMaxTemperature(self, device: dict): Returns: int: Maximum temperature """ - if device["hiveType"] == "nathermostat": - return self.session.data.products[device["hiveID"]]["props"]["maxHeat"] + if device.hive_type == "nathermostat": + return self.session.data.products[device.hive_id]["props"]["maxHeat"] return 32 async def getCurrentTemperature(self, device: dict): @@ -58,7 +58,7 @@ async def getCurrentTemperature(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["temperature"] try: @@ -70,29 +70,28 @@ async def getCurrentTemperature(self, device: dict): device.get("haName", device.get("hiveID")), ) return None - - if device["hiveID"] in self.session.data.minMax: - if self.session.data.minMax[device["hiveID"]]["TodayDate"] == str( + if device.hive_id in self.session.data.minMax: + if self.session.data.minMax[device.hive_id]["TodayDate"] == str( datetime.date(datetime.now()) ): - if state < self.session.data.minMax[device["hiveID"]]["TodayMin"]: - self.session.data.minMax[device["hiveID"]]["TodayMin"] = state + if state < self.session.data.minMax[device.hive_id]["TodayMin"]: + self.session.data.minMax[device.hive_id]["TodayMin"] = state - if state > self.session.data.minMax[device["hiveID"]]["TodayMax"]: - self.session.data.minMax[device["hiveID"]]["TodayMax"] = state + if state > self.session.data.minMax[device.hive_id]["TodayMax"]: + self.session.data.minMax[device.hive_id]["TodayMax"] = state else: data = { "TodayMin": state, "TodayMax": state, "TodayDate": str(datetime.date(datetime.now())), } - self.session.data.minMax[device["hiveID"]].update(data) + self.session.data.minMax[device.hive_id].update(data) - if state < self.session.data.minMax[device["hiveID"]]["RestartMin"]: - self.session.data.minMax[device["hiveID"]]["RestartMin"] = state + if state < self.session.data.minMax[device.hive_id]["RestartMin"]: + self.session.data.minMax[device.hive_id]["RestartMin"] = state - if state > self.session.data.minMax[device["hiveID"]]["RestartMax"]: - self.session.data.minMax[device["hiveID"]]["RestartMax"] = state + if state > self.session.data.minMax[device.hive_id]["RestartMax"]: + self.session.data.minMax[device.hive_id]["RestartMax"] = state else: data = { "TodayMin": state, @@ -101,7 +100,7 @@ async def getCurrentTemperature(self, device: dict): "RestartMin": state, "RestartMax": state, } - self.session.data.minMax[device["hiveID"]] = data + self.session.data.minMax[device.hive_id] = data final = round(state, 1) except KeyError as e: @@ -121,7 +120,7 @@ async def getTargetTemperature(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"].get("target", None) if state is None: state = data["state"].get("heat", None) @@ -154,7 +153,7 @@ async def getMode(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["mode"] if state == "BOOST": state = data["props"]["previous"]["mode"] @@ -202,7 +201,7 @@ async def getCurrentOperation(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["working"] except KeyError as e: _LOGGER.error(e) @@ -221,7 +220,7 @@ async def getBoostStatus(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = HIVETOHA["Boost"].get(data["state"].get("boost", False), "ON") except KeyError as e: _LOGGER.error(e) @@ -241,7 +240,7 @@ async def getBoostTime(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["boost"] except KeyError as e: _LOGGER.error(e) @@ -261,7 +260,7 @@ async def getHeatOnDemand(self, device): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["autoBoost"]["active"] except KeyError as e: _LOGGER.error(e) @@ -291,19 +290,19 @@ async def setTargetTemperature(self, device: dict, new_temp: str): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting target temperature to %s for %s.", new_temp, device["haName"] ) - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], target=new_temp + data["type"], device.hive_id, target=new_temp ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -322,19 +321,19 @@ async def setMode(self, device: dict, new_mode: str): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting heating mode to %s for %s.", new_mode, device["haName"] ) - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], mode=new_mode + data["type"], device.hive_id, mode=new_mode ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -356,8 +355,8 @@ async def setBoostOn(self, device: dict, mins: str, temp: float): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting heating boost ON for %s: %s mins at %s degrees.", @@ -365,17 +364,17 @@ async def setBoostOn(self, device: dict, mins: str, temp: float): mins, temp, ) - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, mode="BOOST", boost=mins, target=temp, ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -393,29 +392,29 @@ async def setBoostOff(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Setting heating boost OFF for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] - await self.session.getDevices(device["hiveID"]) + data = self.session.data.products[device.hive_id] + await self.session.getDevices(device.hive_id) if await self.getBoostStatus(device) == "ON": prev_mode = data["props"]["previous"]["mode"] if prev_mode == "MANUAL" or prev_mode == "OFF": pre_temp = data["props"]["previous"].get("target", 7) resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, mode=prev_mode, target=pre_temp, ) else: resp = await self.session.api.setState( - data["type"], device["hiveID"], mode=prev_mode + data["type"], device.hive_id, mode=prev_mode ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -433,20 +432,20 @@ async def setHeatOnDemand(self, device: dict, state: str): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting heat on demand to %s for %s.", state, device["haName"] ) - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] await self.session.hiveRefreshTokens() resp = await self.session.api.setState( - data["type"], device["hiveID"], autoBoost=state + data["type"], device.hive_id, autoBoost=state ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -476,20 +475,20 @@ async def getClimate(self, device: dict): Returns: dict: Updated device. """ - device["deviceData"].update( + device.device_data.update( {"online": await self.session.attr.onlineOffline(device["device_id"])} ) - if device["deviceData"]["online"]: + if device.device_data["online"]: dev_data = {} self.session.helper.deviceRecovered(device["device_id"]) _LOGGER.debug("Updating climate data for %s.", device["haName"]) data = self.session.data.devices[device["device_id"]] dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, "haType": device["haType"], "device_id": device["device_id"], "device_name": device["device_name"], @@ -507,14 +506,14 @@ async def getClimate(self, device: dict): "parentDevice": data.get("parent", None), "custom": device.get("custom", None), "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] + device["device_id"], device.hive_type ), } - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] + device["device_id"], "ERROR", device.device_data["online"] ) if self.session._lastPollSlow: cached = self.session.devices.get(device["hiveID"]) @@ -552,7 +551,7 @@ async def getScheduleNowNextLater(self, device: dict): try: if online and current_mode == "SCHEDULE": - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = self.session.helper.getScheduleNNL(data["state"]["schedule"]) except KeyError as e: _LOGGER.error(e) @@ -572,7 +571,7 @@ async def minmaxTemperature(self, device: dict): final = None try: - state = self.session.data.minMax[device["hiveID"]] + state = self.session.data.minMax[device.hive_id] final = state except KeyError as e: _LOGGER.error(e) diff --git a/src/helper/const.py b/src/helper/const.py index 3fa5588..b0322e8 100644 --- a/src/helper/const.py +++ b/src/helper/const.py @@ -1,6 +1,7 @@ """Constants for Pyhiveapi.""" -# pylint: skip-file +from .hivedataclasses import EntityConfig + SYNC_PACKAGE_NAME = "pyhiveapi" SYNC_PACKAGE_DIR = "/pyhiveapi/" ASYNC_PACKAGE_NAME = "apyhiveapi" @@ -26,7 +27,6 @@ HIVETOHA = { - "Alarm": {"home": "armed_home", "away": "armed_away", "asleep": "armed_night"}, "Attribute": {True: "Online", False: "Offline"}, "Boost": {None: "OFF", False: "OFF"}, "Heating": {False: "OFF", "ENABLED": True, "DISABLED": False}, @@ -60,7 +60,6 @@ "SMOKE_CO": "self.session.hub.getSmokeStatus(device)", "DOG_BARK": "self.session.hub.getDogBarkStatus(device)", "GLASS_BREAK": "self.session.hub.getGlassBreakStatus(device)", - "Camera_Temp": "self.session.camera.getCameraTemperature(device)", "Current_Temperature": "self.session.heating.getCurrentTemperature(device)", "Heating_Current_Temperature": "self.session.heating.getCurrentTemperature(device)", "Heating_Target_Temperature": "self.session.heating.getTargetTemperature(device)", @@ -71,7 +70,7 @@ "Hotwater_Mode": "self.session.hotwater.getMode(device)", "Hotwater_Boost": "self.session.hotwater.getBoost(device)", "Battery": 'self.session.attr.getBattery(device["device_id"])', - "Mode": 'self.session.attr.getMode(device["hiveID"])', + "Mode": "self.session.attr.getMode(device.hive_id)", "Availability": "self.online(device)", "Connectivity": "self.online(device)", "Power": "self.session.switch.getPowerUsage(device)", @@ -79,93 +78,269 @@ PRODUCTS = { "sense": [ - 'addList("binary_sensor", p, haName="Glass Detection", hiveType="GLASS_BREAK")', - 'addList("binary_sensor", p, haName="Smoke Detection", hiveType="SMOKE_CO")', - 'addList("binary_sensor", p, haName="Dog Bark Detection", hiveType="DOG_BARK")', + EntityConfig( + entity_type="binary_sensor", + ha_name="Glass Detection", + hive_type="GLASS_BREAK", + ), + EntityConfig( + entity_type="binary_sensor", ha_name="Smoke Detection", hive_type="SMOKE_CO" + ), + EntityConfig( + entity_type="binary_sensor", + ha_name="Dog Bark Detection", + hive_type="DOG_BARK", + ), ], "heating": [ - 'addList("climate", p, temperatureunit=self.data["user"]["temperatureUnit"])', - 'addList("switch", p, haName=" Heat on Demand", hiveType="Heating_Heat_On_Demand", category="config")', - 'addList("sensor", p, haName=" Current Temperature", hiveType="Heating_Current_Temperature", category="diagnostic")', - 'addList("sensor", p, haName=" Target Temperature", hiveType="Heating_Target_Temperature", category="diagnostic")', - 'addList("sensor", p, haName=" State", hiveType="Heating_State", category="diagnostic")', - 'addList("sensor", p, haName=" Mode", hiveType="Heating_Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Boost", hiveType="Heating_Boost", category="diagnostic")', + EntityConfig(entity_type="climate", temperature_unit="user.temperatureUnit"), + EntityConfig( + entity_type="switch", + ha_name="Heat on Demand", + hive_type="Heating_Heat_On_Demand", + category="config", + ), + EntityConfig( + entity_type="sensor", + ha_name="Current Temperature", + hive_type="Heating_Current_Temperature", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Target Temperature", + hive_type="Heating_Target_Temperature", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="State", + hive_type="Heating_State", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Mode", + hive_type="Heating_Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Boost", + hive_type="Heating_Boost", + category="diagnostic", + ), ], "trvcontrol": [ - 'addList("climate", p, temperatureunit=self.data["user"]["temperatureUnit"])', - 'addList("sensor", p, haName=" Current Temperature", hiveType="Heating_Current_Temperature", category="diagnostic")', - 'addList("sensor", p, haName=" Target Temperature", hiveType="Heating_Target_Temperature", category="diagnostic")', - 'addList("sensor", p, haName=" State", hiveType="Heating_State", category="diagnostic")', - 'addList("sensor", p, haName=" Mode", hiveType="Heating_Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Boost", hiveType="Heating_Boost", category="diagnostic")', + EntityConfig(entity_type="climate", temperature_unit="user.temperatureUnit"), + EntityConfig( + entity_type="switch", + ha_name="Heat on Demand", + hive_type="Heating_Heat_On_Demand", + category="config", + ), + EntityConfig( + entity_type="sensor", + ha_name="Current Temperature", + hive_type="Heating_Current_Temperature", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Target Temperature", + hive_type="Heating_Target_Temperature", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="State", + hive_type="Heating_State", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Mode", + hive_type="Heating_Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Boost", + hive_type="Heating_Boost", + category="diagnostic", + ), ], "hotwater": [ - 'addList("water_heater", p,)', - 'addList("sensor", p, haName="Hotwater State", hiveType="Hotwater_State", category="diagnostic")', - 'addList("sensor", p, haName="Hotwater Mode", hiveType="Hotwater_Mode", category="diagnostic")', - 'addList("sensor", p, haName="Hotwater Boost", hiveType="Hotwater_Boost", category="diagnostic")', + EntityConfig( + entity_type="water_heater", + ha_name="Hotwater State", + hive_type="Hotwater_State", + ), + EntityConfig( + entity_type="sensor", + ha_name="Hotwater Mode", + hive_type="Hotwater_Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name="Hotwater Boost", + hive_type="Hotwater_Boost", + category="diagnostic", + ), ], "activeplug": [ - 'addList("switch", p)', - 'addList("sensor", p, haName=" Mode", hiveType="Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Availability", hiveType="Availability", category="diagnostic")', - 'addList("sensor", p, haName=" Power", hiveType="Power", category="diagnostic")', + EntityConfig(entity_type="switch"), + EntityConfig( + entity_type="sensor", + ha_name=" Mode", + hive_type="Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Power", + hive_type="Power", + category="diagnostic", + ), ], "warmwhitelight": [ - 'addList("light", p)', - 'addList("sensor", p, haName=" Mode", hiveType="Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig(entity_type="light"), + EntityConfig( + entity_type="sensor", + ha_name=" Mode", + hive_type="Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], "tuneablelight": [ - 'addList("light", p)', - 'addList("sensor", p, haName=" Mode", hiveType="Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig(entity_type="light"), + EntityConfig( + entity_type="sensor", + ha_name=" Mode", + hive_type="Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], "colourtuneablelight": [ - 'addList("light", p)', - 'addList("sensor", p, haName=" Mode", hiveType="Mode", category="diagnostic")', - 'addList("sensor", p, haName=" Availability", hiveType="Availability", category="diagnostic")', - ], - # "hivecamera": [ - # 'addList("camera", p)', - # 'addList("sensor", p, haName=" Mode", hiveType="Mode", category="diagnostic")', - # 'addList("sensor", p, haName=" Availability", hiveType="Availability", category="diagnostic")', - # 'addList("sensor", p, haName=" Temperature", hiveType="Camera_Temp", category="diagnostic")', - # ], + EntityConfig(entity_type="light"), + EntityConfig( + entity_type="sensor", + ha_name=" Mode", + hive_type="Mode", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), + ], "motionsensor": [ - 'addList("binary_sensor", p)', - 'addList("sensor", p, haName=" Current Temperature", hiveType="Current_Temperature", category="diagnostic")', + EntityConfig(entity_type="binary_sensor"), + EntityConfig( + entity_type="sensor", + ha_name=" Current Temperature", + hive_type="Current_Temperature", + category="diagnostic", + ), + ], + "contactsensor": [ + EntityConfig(entity_type="binary_sensor"), ], - "contactsensor": ['addList("binary_sensor", p)'], } DEVICES = { "contactsensor": [ - 'addList("sensor", d, haName=" Battery Level", hiveType="Battery", category="diagnostic")', - 'addList("sensor", d, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig( + entity_type="sensor", + ha_name=" Battery Level", + hive_type="Battery", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], "hub": [ - 'addList("binary_sensor", d, haName="Hive Hub Status", hiveType="Connectivity", category="diagnostic")', + EntityConfig( + entity_type="binary_sensor", + ha_name="Hive Hub Status", + hive_type="Connectivity", + category="diagnostic", + ), ], "motionsensor": [ - 'addList("sensor", d, haName=" Battery Level", hiveType="Battery", category="diagnostic")', - 'addList("sensor", d, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig( + entity_type="sensor", + ha_name=" Battery Level", + hive_type="Battery", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], "sense": [ - 'addList("binary_sensor", d, haName="Hive Hub Status", hiveType="Connectivity")', + EntityConfig( + entity_type="binary_sensor", + ha_name="Hive Hub Status", + hive_type="Connectivity", + ), ], - "siren": ['addList("alarm_control_panel", d)'], "thermostatui": [ - 'addList("sensor", d, haName=" Battery Level", hiveType="Battery", category="diagnostic")', - 'addList("sensor", d, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig( + entity_type="sensor", + ha_name=" Battery Level", + hive_type="Battery", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], "trv": [ - 'addList("sensor", d, haName=" Battery Level", hiveType="Battery", category="diagnostic")', - 'addList("sensor", d, haName=" Availability", hiveType="Availability", category="diagnostic")', + EntityConfig( + entity_type="sensor", + ha_name=" Battery Level", + hive_type="Battery", + category="diagnostic", + ), + EntityConfig( + entity_type="sensor", + ha_name=" Availability", + hive_type="Availability", + category="diagnostic", + ), ], } -ACTIONS = ( - 'addList("switch", a, hiveName=a["name"], haName=a["name"], hiveType="action")' -) +ACTIONS = EntityConfig(entity_type="switch", ha_name="action.name", hive_type="action") diff --git a/src/helper/debugger.py b/src/helper/debugger.py index f3a3102..14934a7 100644 --- a/src/helper/debugger.py +++ b/src/helper/debugger.py @@ -1,7 +1,7 @@ """Debugger file.""" -# pylint: skip-file import logging +import sys class DebugContext: @@ -12,43 +12,47 @@ def __init__(self, name, enabled): self.name = name self.enabled = enabled self.logging = logging.getLogger(__name__) - self.debugOutFolder = "" - self.debugOutFile = "" - self.debugEnabled = False - self.debugList = [] + self.debug_out_folder = "" + self.debug_out_file = "" + self.debug_enabled = False + self.debug_list = [] def __enter__(self): """Set trace calls on entering debugger.""" print("Entering Debug Decorated func") # Set the trace function to the trace_calls function # So all events are now traced - self.traceCalls + sys.settrace(self.trace_calls) + return self - def traceCalls(self, frame, event, arg): + def __exit__(self, exc_type, exc_val, exc_tb): + """Clean up trace on exiting debugger.""" + print("Exiting Debug Decorated func") + sys.settrace(None) + return False + + def trace_calls(self, frame, event, _arg): """Trace calls be made.""" # We want to only trace our call to the decorated function if event != "call": - return - elif frame.f_code.co_name != self.name: - return + return None + if frame.f_code.co_name != self.name: + return None # return the trace function to use when you go into that # function call - return self.traceLines + return self.trace_lines - def traceLines(self, frame, event, arg): + def trace_lines(self, frame, event, _arg): """Print out lines for function.""" - # If you want to print local variables each line - # keep the check for the event 'line' - # If you want to print local variables only on return - # check only for the 'return' event if event not in ["line", "return"]: - return + return None co = frame.f_code func_name = co.co_name line_no = frame.f_lineno local_vars = frame.f_locals text = f" {func_name} {event} {line_no} locals: {local_vars}" self.logging.debug(text) + return self.trace_lines def debug(enabled=False): diff --git a/src/helper/hive_helper.py b/src/helper/hive_helper.py index 3ada8b7..24e9f6e 100644 --- a/src/helper/hive_helper.py +++ b/src/helper/hive_helper.py @@ -6,6 +6,7 @@ import operator from .const import HIVE_TYPES +from .hivedataclasses import EntityConfig _LOGGER = logging.getLogger(__name__) @@ -228,6 +229,21 @@ def getHeatOnDemandDevice(self, device: dict): Returns: [dictionary]: [Gets the thermostat device linked to TRV.] """ - trv = self.session.data.products.get(device["HiveID"]) + trv = self.session.data.products.get(device.hive_id) thermostat = self.session.data.products.get(trv["state"]["zone"]) return thermostat + + def _build_kwargs(self, config: EntityConfig) -> dict: + """Build kwargs dict from EntityConfig, excluding None values.""" + kwargs = {} + + if config.ha_name: + kwargs["haName"] = config.ha_name + if config.hive_type: + kwargs["hiveType"] = config.hive_type + if config.category: + kwargs["category"] = config.category + if config.temperature_unit: + kwargs["temperatureunit"] = self.session.data["user"]["temperatureUnit"] + + return kwargs diff --git a/src/helper/hivedataclasses.py b/src/helper/hivedataclasses.py index 184b3cb..78b57da 100644 --- a/src/helper/hivedataclasses.py +++ b/src/helper/hivedataclasses.py @@ -1,22 +1,43 @@ """Device data class.""" -# pylint: skip-file - from dataclasses import dataclass +from typing import Literal, Optional @dataclass class Device: - """Class for keeping track of an device.""" + """Class for keeping track of a device.""" - hiveID: str - hiveName: str - hiveType: str - haType: str - deviceData: dict - status: dict - data: dict - parentDevice: str - isGroup: bool + hive_id: str + hive_name: str + hive_type: str + ha_type: str device_id: str device_name: str + device_data: dict + parent_device: Optional[str] = None + is_group: bool = False + ha_name: str = "" + category: Optional[str] = None + temperature_unit: Optional[str] = None + # Add status and data as optional since not always populated at creation + status: Optional[dict] = None + data: Optional[dict] = None + + +@dataclass +class EntityConfig: + """Configuration for creating a device entity.""" + + entity_type: Literal[ + "sensor", + "binary_sensor", + "climate", + "light", + "switch", + "water_heater", + ] + ha_name: str = "" + hive_type: str = "" + category: Optional[str] = None + temperature_unit: Optional[str] = None diff --git a/src/helper/map.py b/src/helper/map.py index b4bd8ea..8014b10 100644 --- a/src/helper/map.py +++ b/src/helper/map.py @@ -1,7 +1,5 @@ """Dot notation for dictionary.""" -# pylint: skip-file - class Map(dict): """dot.notation access to dictionary attributes. diff --git a/src/hive.py b/src/hive.py index 29f2edd..75558bb 100644 --- a/src/hive.py +++ b/src/hive.py @@ -10,8 +10,6 @@ from aiohttp import ClientSession from .action import HiveAction -from .alarm import Alarm -from .camera import Camera from .heating import Climate from .hotwater import WaterHeater from .hub import HiveHub @@ -105,8 +103,6 @@ def __init__( super().__init__(username, password, websession) self.session = self self.action = HiveAction(self.session) - self.alarm = Alarm(self.session) - self.camera = Camera(self.session) self.heating = Climate(self.session) self.hotwater = WaterHeater(self.session) self.hub = HiveHub(self.session) diff --git a/src/hotwater.py b/src/hotwater.py index bd57e27..dc24df6 100644 --- a/src/hotwater.py +++ b/src/hotwater.py @@ -30,7 +30,7 @@ async def getMode(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["mode"] if state == "BOOST": state = data["props"]["previous"]["mode"] @@ -62,7 +62,7 @@ async def getBoost(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["boost"] final = HIVETOHA["Boost"].get(state, "ON") except KeyError as e: @@ -82,7 +82,7 @@ async def getBoostTime(self, device: dict): state = None if await self.getBoost(device) == "ON": try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["boost"] except KeyError as e: _LOGGER.error(e) @@ -102,7 +102,7 @@ async def getState(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["status"] mode_current = await self.getMode(device) if mode_current == "SCHEDULE": @@ -130,18 +130,18 @@ async def setMode(self, device: dict, new_mode: str): """ final = False - if device["hiveID"] in self.session.data.products: + if device.hive_id in self.session.data.products: _LOGGER.debug( "Setting hot water mode to %s for %s.", new_mode, device["haName"] ) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], mode=new_mode + data["type"], device.hive_id, mode=new_mode ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -159,20 +159,20 @@ async def setBoostOn(self, device: dict, mins: int): if ( int(mins) > 0 - and device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + and device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting hot water boost ON for %s: %s mins.", device["haName"], mins ) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], mode="BOOST", boost=mins + data["type"], device.hive_id, mode="BOOST", boost=mins ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -188,19 +188,19 @@ async def setBoostOff(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products + device.hive_id in self.session.data.products and await self.getBoost(device) == "ON" - and device["deviceData"]["online"] + and device.device_data["online"] ): _LOGGER.debug("Setting hot water boost OFF for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] prev_mode = data["props"]["previous"]["mode"] resp = await self.session.api.setState( - data["type"], device["hiveID"], mode=prev_mode + data["type"], device.hive_id, mode=prev_mode ) if resp["original"] == 200: - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) final = True return final @@ -230,21 +230,21 @@ async def getWaterHeater(self, device: dict): Returns: dict: Updated device. """ - device["deviceData"].update( + device.device_data.update( {"online": await self.session.attr.onlineOffline(device["device_id"])} ) - if device["deviceData"]["online"]: + if device.device_data["online"]: dev_data = {} self.session.helper.deviceRecovered(device["device_id"]) _LOGGER.debug("Updating hot water data for %s.", device["haName"]) data = self.session.data.devices[device["device_id"]] dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, "haType": device["haType"], "device_id": device["device_id"], "device_name": device["device_name"], @@ -253,15 +253,15 @@ async def getWaterHeater(self, device: dict): "parentDevice": data.get("parent", None), "custom": device.get("custom", None), "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] + device["device_id"], device.hive_type ), } - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] + device["device_id"], "ERROR", device.device_data["online"] ) if self.session._lastPollSlow: cached = self.session.devices.get(device["hiveID"]) @@ -288,7 +288,7 @@ async def getScheduleNowNextLater(self, device: dict): try: mode_current = await self.getMode(device) if mode_current == "SCHEDULE": - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = self.session.helper.getScheduleNNL(data["state"]["schedule"]) except KeyError as e: _LOGGER.error(e) diff --git a/src/hub.py b/src/hub.py index 6af5feb..4a1d97f 100644 --- a/src/hub.py +++ b/src/hub.py @@ -39,7 +39,7 @@ async def getSmokeStatus(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["sensors"]["SMOKE_CO"]["active"] final = HIVETOHA[self.hubType]["Smoke"].get(state, state) except KeyError as e: @@ -60,7 +60,7 @@ async def getDogBarkStatus(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["sensors"]["DOG_BARK"]["active"] final = HIVETOHA[self.hubType]["Dog"].get(state, state) except KeyError as e: @@ -81,7 +81,7 @@ async def getGlassBreakStatus(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["sensors"]["GLASS_BREAK"]["active"] final = HIVETOHA[self.hubType]["Glass"].get(state, state) except KeyError as e: diff --git a/src/light.py b/src/light.py index 7bfd60c..09b80f7 100644 --- a/src/light.py +++ b/src/light.py @@ -31,7 +31,7 @@ async def getState(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["status"] final = HIVETOHA[self.lightType].get(state, state) except KeyError as e: @@ -52,7 +52,7 @@ async def getBrightness(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["brightness"] final = (state / 100) * 255 except KeyError as e: @@ -73,7 +73,7 @@ async def getMinColorTemp(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["colourTemperature"]["max"] final = round((1 / state) * 1000000) except KeyError as e: @@ -94,7 +94,7 @@ async def getMaxColorTemp(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["colourTemperature"]["min"] final = round((1 / state) * 1000000) except KeyError as e: @@ -115,7 +115,7 @@ async def getColorTemp(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["colourTemperature"] final = round((1 / state) * 1000000) except KeyError as e: @@ -136,7 +136,7 @@ async def getColor(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = [ (data["state"]["hue"]) / 360, (data["state"]["saturation"]) / 100, @@ -162,7 +162,7 @@ async def getColorMode(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["colourMode"] except KeyError as e: _LOGGER.error(e) @@ -181,19 +181,19 @@ async def setStatusOff(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Turning light OFF for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], status="OFF" + data["type"], device.hive_id, status="OFF" ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -209,19 +209,19 @@ async def setStatusOn(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Turning light ON for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( - data["type"], device["hiveID"], status="ON" + data["type"], device.hive_id, status="ON" ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -238,23 +238,23 @@ async def setBrightness(self, device: dict, n_brightness: int): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting brightness to %s for %s.", n_brightness, device["haName"] ) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, status="ON", brightness=n_brightness, ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -271,32 +271,32 @@ async def setColorTemp(self, device: dict, color_temp: int): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug( "Setting colour temperature to %s for %s.", color_temp, device["haName"] ) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] if data["type"] == "tuneablelight": resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, colourTemperature=color_temp, ) else: resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, colourMode="WHITE", colourTemperature=color_temp, ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -313,16 +313,16 @@ async def setColor(self, device: dict, new_color: list): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Setting colour to %s for %s.", new_color, device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( data["type"], - device["hiveID"], + device.hive_id, colourMode="COLOUR", hue=str(new_color[0]), saturation=str(new_color[1]), @@ -330,7 +330,7 @@ async def setColor(self, device: dict, new_color: list): ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -359,20 +359,20 @@ async def getLight(self, device: dict): Returns: dict: Updated device. """ - device["deviceData"].update( + device.device_data.update( {"online": await self.session.attr.onlineOffline(device["device_id"])} ) dev_data = {} - if device["deviceData"]["online"]: + if device.device_data["online"]: self.session.helper.deviceRecovered(device["device_id"]) _LOGGER.debug("Updating light data for %s.", device["haName"]) data = self.session.data.devices[device["device_id"]] dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, "haType": device["haType"], "device_id": device["device_id"], "device_name": device["device_name"], @@ -384,11 +384,11 @@ async def getLight(self, device: dict): "parentDevice": data.get("parent", None), "custom": device.get("custom", None), "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] + device["device_id"], device.hive_type ), } - if device["hiveType"] in ("tuneablelight", "colourtuneablelight"): + if device.hive_type in ("tuneablelight", "colourtuneablelight"): dev_data.update( { "min_mireds": await self.getMinColorTemp(device), @@ -398,7 +398,7 @@ async def getLight(self, device: dict): dev_data["status"].update( {"color_temp": await self.getColorTemp(device)} ) - if device["hiveType"] == "colourtuneablelight": + if device.hive_type == "colourtuneablelight": mode = await self.getColorMode(device) if mode == "COLOUR": dev_data["status"].update( @@ -414,11 +414,11 @@ async def getLight(self, device: dict): } ) - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] + device["device_id"], "ERROR", device.device_data["online"] ) if self.session._lastPollSlow: cached = self.session.devices.get(device["hiveID"]) diff --git a/src/plug.py b/src/plug.py index cdf7064..b734016 100644 --- a/src/plug.py +++ b/src/plug.py @@ -29,7 +29,7 @@ async def getState(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["state"]["status"] state = HIVETOHA["Switch"].get(state, state) except KeyError as e: @@ -49,7 +49,7 @@ async def getPowerUsage(self, device: dict): state = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] state = data["props"]["powerConsumption"] except KeyError as e: _LOGGER.error(e) @@ -68,18 +68,18 @@ async def setStatusOn(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Turning plug ON for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( data["type"], data["id"], status="ON" ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -95,18 +95,18 @@ async def setStatusOff(self, device: dict): final = False if ( - device["hiveID"] in self.session.data.products - and device["deviceData"]["online"] + device.hive_id in self.session.data.products + and device.device_data["online"] ): _LOGGER.debug("Turning plug OFF for %s.", device["haName"]) await self.session.hiveRefreshTokens() - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] resp = await self.session.api.setState( data["type"], data["id"], status="OFF" ) if resp["original"] == 200: final = True - await self.session.getDevices(device["hiveID"]) + await self.session.getDevices(device.hive_id) return final @@ -135,20 +135,20 @@ async def getSwitch(self, device: dict): Returns: dict: Return device after update is complete. """ - device["deviceData"].update( + device.device_data.update( {"online": await self.session.attr.onlineOffline(device["device_id"])} ) dev_data = {} - if device["deviceData"]["online"]: + if device.device_data["online"]: self.session.helper.deviceRecovered(device["device_id"]) _LOGGER.debug("Updating switch data for %s.", device["haName"]) data = self.session.data.devices[device["device_id"]] dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, "haType": device["haType"], "device_id": device["device_id"], "device_name": device["device_name"], @@ -161,7 +161,7 @@ async def getSwitch(self, device: dict): "attributes": {}, } - if device["hiveType"] == "activeplug": + if device.hive_type == "activeplug": dev_data.update( { "status": { @@ -169,16 +169,16 @@ async def getSwitch(self, device: dict): "power_usage": await self.getPowerUsage(device), }, "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] + device["device_id"], device.hive_type ), } ) - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] + device["device_id"], "ERROR", device.device_data["online"] ) if self.session._lastPollSlow: cached = self.session.devices.get(device["hiveID"]) @@ -200,7 +200,7 @@ async def getSwitchState(self, device: dict): Returns: boolean: Return True or False for the state. """ - if device["hiveType"] == "Heating_Heat_On_Demand": + if device.hive_type == "Heating_Heat_On_Demand": return await self.session.heating.getHeatOnDemand(device) else: return await self.getState(device) @@ -214,7 +214,7 @@ async def turnOn(self, device: dict): Returns: function: Calls relevant function. """ - if device["hiveType"] == "Heating_Heat_On_Demand": + if device.hive_type == "Heating_Heat_On_Demand": return await self.session.heating.setHeatOnDemand(device, "ENABLED") else: return await self.setStatusOn(device) @@ -228,7 +228,7 @@ async def turnOff(self, device: dict): Returns: function: Calls relevant function. """ - if device["hiveType"] == "Heating_Heat_On_Demand": + if device.hive_type == "Heating_Heat_On_Demand": return await self.session.heating.setHeatOnDemand(device, "DISABLED") else: return await self.setStatusOff(device) diff --git a/src/sensor.py b/src/sensor.py index e0ba2a7..4445429 100644 --- a/src/sensor.py +++ b/src/sensor.py @@ -26,7 +26,7 @@ async def getState(self, device: dict): final = None try: - data = self.session.data.products[device["hiveID"]] + data = self.session.data.products[device.hive_id] if data["type"] == "contactsensor": state = data["props"]["status"] final = HIVETOHA[self.sensorType].get(state, state) @@ -83,16 +83,16 @@ async def getSensor(self, device: dict): Returns: dict: Updated device. """ - device["deviceData"].update( + device.device_data.update( {"online": await self.session.attr.onlineOffline(device["device_id"])} ) data = {} - if device["deviceData"]["online"] or device["hiveType"] in ( + if device.device_data["online"] or device.hive_type in ( "Availability", "Connectivity", ): - if device["hiveType"] not in ("Availability", "Connectivity"): + if device.hive_type not in ("Availability", "Connectivity"): self.session.helper.deviceRecovered(device["device_id"]) _LOGGER.debug( @@ -102,10 +102,10 @@ async def getSensor(self, device: dict): ) dev_data = {} dev_data = { - "hiveID": device["hiveID"], - "hiveName": device["hiveName"], - "hiveType": device["hiveType"], - "haName": device["haName"], + "hiveID": device.hive_id, + "hiveName": device.hive_name, + "hiveType": device.hive_type, + "haName": device.ha_name, "haType": device["haType"], "device_id": device.get("device_id", None), "device_name": device.get("device_name", None), @@ -115,8 +115,8 @@ async def getSensor(self, device: dict): if device["device_id"] in self.session.data.devices: data = self.session.data.devices.get(device["device_id"], {}) - elif device["hiveID"] in self.session.data.products: - data = self.session.data.products.get(device["hiveID"], {}) + elif device.hive_id in self.session.data.products: + data = self.session.data.products.get(device.hive_id, {}) if ( dev_data["hiveType"] in sensor_commands @@ -133,24 +133,24 @@ async def getSensor(self, device: dict): "parentDevice": data.get("parent", None), } ) - elif device["hiveType"] in HIVE_TYPES["Sensor"]: - data = self.session.data.devices.get(device["hiveID"], {}) + elif device.hive_type in HIVE_TYPES["Sensor"]: + data = self.session.data.devices.get(device.hive_id, {}) dev_data.update( { "status": {"state": await self.getState(device)}, "deviceData": data.get("props", None), "parentDevice": data.get("parent", None), "attributes": await self.session.attr.stateAttributes( - device["device_id"], device["hiveType"] + device["device_id"], device.hive_type ), } ) - self.session.devices.update({device["hiveID"]: dev_data}) - return self.session.devices[device["hiveID"]] + self.session.devices.update({device.hive_id: dev_data}) + return self.session.devices[device.hive_id] else: await self.session.helper.errorCheck( - device["device_id"], "ERROR", device["deviceData"]["online"] + device["device_id"], "ERROR", device.device_data["online"] ) if self.session._lastPollSlow: cached = self.session.devices.get(device["hiveID"]) diff --git a/src/session.py b/src/session.py index af7b957..9ef076c 100644 --- a/src/session.py +++ b/src/session.py @@ -9,13 +9,14 @@ import os import time from datetime import datetime, timedelta +from typing import Optional from aiohttp.web import HTTPException from apyhiveapi import API, Auth from .device_attributes import HiveAttributes -from .helper.const import ACTIONS, DEVICES, HIVE_TYPES, PRODUCTS +from .helper.const import DEVICES, HIVE_TYPES, PRODUCTS from .helper.hive_exceptions import ( HiveApiError, HiveAuthError, @@ -27,9 +28,9 @@ HiveReauthRequired, HiveRefreshTokenExpired, HiveUnknownConfiguration, - NoApiToken, ) from .helper.hive_helper import HiveHelper +from .helper.hivedataclasses import Device from .helper.map import Map _LOGGER = logging.getLogger(__name__) @@ -74,23 +75,21 @@ def __init__( self._refreshLock = asyncio.Lock() self.tokens = Map( { - "tokenData": {}, - "tokenCreated": datetime.min, - "tokenExpiry": timedelta(seconds=3600), + "token_data": {}, + "token_created": datetime.min, + "token_expiry": timedelta(seconds=3600), } ) self.config = Map( { - "alarm": False, "battery": [], - "camera": False, "errorList": {}, "file": False, - "homeID": None, - "lastUpdate": datetime.now(), + "home_id": None, + "last_update": datetime.now(), "mode": [], - "scanInterval": timedelta(seconds=120), - "userID": None, + "scan_interval": timedelta(seconds=120), + "user_id": None, "username": username, } ) @@ -101,16 +100,14 @@ def __init__( "actions": {}, "user": {}, "minMax": {}, - "alarm": {}, - "camera": {}, } ) self.devices = {} - self.deviceList = {} + self.device_list = {} self.hub_id = None self._lastPollSlow = False self._slowPollThreshold = 3 - self._refreshThreshold = 0.90 + self._refresh_threshold = 0.90 def openFile(self, file: str): """Open a file. @@ -128,15 +125,16 @@ def openFile(self, file: str): return data - def addList(self, entityType: str, data: dict, **kwargs: dict): + def addList(self, entity_type: str, data: dict, **kwargs: dict) -> Optional[Device]: """Add entity to the list. Args: - type (str): Type of entity + entityType (str): Type of entity data (dict): Information to create entity. + **kwargs: Additional device attributes (ha_name, category, etc.) Returns: - dict: Entity. + Optional[Device]: Device dataclass instance, or None on error. """ try: device = self.helper.getDeviceData(data) @@ -145,34 +143,35 @@ def addList(self, entityType: str, data: dict, **kwargs: dict): if device["state"]["name"] != "Receiver" else "Heating" ) - formatted_data = {} - - formatted_data = { - "hiveID": data.get("id", ""), - "hiveName": device_name, - "hiveType": data.get("type", ""), - "haType": entityType, - "deviceData": device.get("props", data.get("props", {})), - "parentDevice": self.hub_id, - "isGroup": data.get("isGroup", False), - "device_id": device["id"], - "device_name": device_name, - } - - if kwargs.get("haName", "FALSE")[0] == " ": - kwargs["haName"] = device_name + kwargs["haName"] - else: - formatted_data["haName"] = device_name - formatted_data.update(kwargs) + # Handle ha_name logic + ha_name = kwargs.get("ha_name", device_name) + if ha_name and ha_name[0] == " ": + ha_name = device_name + ha_name + + # Create Device instance + device_obj = Device( + hive_id=data.get("id", ""), + hive_name=device_name, + hive_type=data.get("type", ""), + ha_type=entity_type, + device_id=device["id"], + device_name=device_name, + device_data=device.get("props", data.get("props", {})), + parent_device=self.hub_id, + is_group=data.get("isGroup", False), + ha_name=ha_name, + category=kwargs.get("category"), + temperature_unit=kwargs.get("temperatureunit"), + ) if data.get("type", "") == "hub": - self.deviceList["parent"].append(formatted_data) - self.deviceList[entityType].append(formatted_data) + self.device_list["parent_device"].append(device_obj) + self.device_list[entity_type].append(device_obj) else: - self.deviceList[entityType].append(formatted_data) + self.device_list[entity_type].append(device_obj) - return formatted_data + return device_obj except KeyError as error: _LOGGER.error(error) return None @@ -189,7 +188,7 @@ async def updateInterval(self, new_interval: timedelta): interval = new_interval if interval < timedelta(seconds=15): interval = timedelta(seconds=15) - self.config.scanInterval = interval + self.config.scan_interval = interval async def useFile(self, username: str = None): """Update to check if file is being used. @@ -214,42 +213,42 @@ async def updateTokens(self, tokens: dict, update_expiry_time: bool = True): data = {} if "AuthenticationResult" in tokens: data = tokens.get("AuthenticationResult") - self.tokens.tokenData.update({"token": data["IdToken"]}) + self.tokens.token_data.update({"token": data["IdToken"]}) if "RefreshToken" in data: - self.tokens.tokenData.update({"refreshToken": data["RefreshToken"]}) - self.tokens.tokenData.update({"accessToken": data["AccessToken"]}) + self.tokens.token_data.update({"refreshToken": data["RefreshToken"]}) + self.tokens.token_data.update({"accessToken": data["AccessToken"]}) if update_expiry_time: - self.tokens.tokenCreated = datetime.now() + self.tokens.token_created = datetime.now() elif "token" in tokens: data = tokens - self.tokens.tokenData.update({"token": data["token"]}) - self.tokens.tokenData.update({"refreshToken": data["refreshToken"]}) - self.tokens.tokenData.update({"accessToken": data["accessToken"]}) + self.tokens.token_data.update({"token": data["token"]}) + self.tokens.token_data.update({"refreshToken": data["refreshToken"]}) + self.tokens.token_data.update({"accessToken": data["accessToken"]}) if "ExpiresIn" in data: - self.tokens.tokenExpiry = timedelta(seconds=data["ExpiresIn"]) + self.tokens.token_expiry = timedelta(seconds=data["ExpiresIn"]) _LOGGER.debug( "updateTokens — IdToken: len=%d tail=…%s | " "AccessToken: len=%d tail=…%s | " "RefreshToken: %s | " "ExpiresIn: %s | tokenCreated: %s | tokenExpiry: %s", - len(self.tokens.tokenData.get("token", "")), - self.tokens.tokenData.get("token", "")[-4:], - len(self.tokens.tokenData.get("accessToken", "")), - self.tokens.tokenData.get("accessToken", "")[-4:], + len(self.tokens.token_data.get("token", "")), + self.tokens.token_data.get("token", "")[-4:], + len(self.tokens.token_data.get("accessToken", "")), + self.tokens.token_data.get("accessToken", "")[-4:], ( "present (len=%d tail=…%s)" % ( - len(self.tokens.tokenData.get("refreshToken", "")), - self.tokens.tokenData.get("refreshToken", "")[-4:], + len(self.tokens.token_data.get("refreshToken", "")), + self.tokens.token_data.get("refreshToken", "")[-4:], ) - if self.tokens.tokenData.get("refreshToken") + if self.tokens.token_data.get("refreshToken") else "not present" ), data.get("ExpiresIn", "N/A"), - self.tokens.tokenCreated, - self.tokens.tokenExpiry, + self.tokens.token_created, + self.tokens.token_expiry, ) return self.tokens @@ -393,8 +392,8 @@ async def hiveRefreshTokens(self, force_refresh: bool = False): if self.config.file: return None else: - expiry_time = self.tokens.tokenCreated + ( - self.tokens.tokenExpiry * self._refreshThreshold + expiry_time = self.tokens.token_created + ( + self.tokens.token_expiry * self._refresh_threshold ) # Refresh at 90% of token lifetime to prevent expiration during API calls _LOGGER.debug( @@ -405,25 +404,25 @@ async def hiveRefreshTokens(self, force_refresh: bool = False): if datetime.now() >= expiry_time or force_refresh: async with self._refreshLock: # Re-check after acquiring lock — another caller may have already refreshed - expiry_time = self.tokens.tokenCreated + ( - self.tokens.tokenExpiry * self._refreshThreshold + expiry_time = self.tokens.token_created + ( + self.tokens.token_expiry * self._refresh_threshold ) if datetime.now() < expiry_time and not force_refresh: return result - actual_expiry = self.tokens.tokenCreated + self.tokens.tokenExpiry + actual_expiry = self.tokens.token_created + self.tokens.token_expiry _LOGGER.debug( "Session Token created: %s | Actual expiry: %s | " "Early refresh (×%s): %s | Now: %s | Force refresh: %s", - self.tokens.tokenCreated, + self.tokens.token_created, actual_expiry, - self._refreshThreshold, + self._refresh_threshold, expiry_time, datetime.now(), force_refresh, ) try: result = await self.auth.refresh_token( - self.tokens.tokenData["refreshToken"] + self.tokens.token_data["refreshToken"] ) if result and "AuthenticationResult" in result: @@ -434,7 +433,7 @@ async def hiveRefreshTokens(self, force_refresh: bool = False): ) await self.updateTokens(result) new_expiry = ( - self.tokens.tokenCreated + self.tokens.tokenExpiry + self.tokens.token_created + self.tokens.token_expiry ) _LOGGER.debug( "Session Token refresh successful. New expiry: %s", @@ -468,18 +467,15 @@ async def updateData(self, device: dict): boolean: True/False if update was successful """ updated = False - ep = self.config.lastUpdate + self.config.scanInterval + ep = self.config.last_update + self.config.scan_interval if datetime.now() >= ep: async with self.updateLock: # Re-check after acquiring lock — another caller may have already updated - ep = self.config.lastUpdate + self.config.scanInterval + ep = self.config.last_update + self.config.scan_interval if datetime.now() < ep: return updated _LOGGER.debug("Polling Hive API for device updates.") updated = await self.getDevices(device["hiveID"]) - if updated and len(self.deviceList["camera"]) > 0: - for camera in self.data.camera: - await self.getCamera(self.devices[camera]) if updated: _LOGGER.debug("Device update completed successfully.") else: @@ -489,72 +485,6 @@ async def updateData(self, device: dict): return updated - async def getAlarm(self): - """Get alarm data. - - Raises: - HTTPException: HTTP error has occurred updating the devices. - HiveApiError: An API error code has been returned. - """ - if self.config.file: - api_resp_d = self.openFile("alarm.json") - elif self.tokens is not None: - api_resp_d = await self.api.getAlarm() - if operator.contains(str(api_resp_d["original"]), "20") is False: - raise HTTPException - elif api_resp_d["parsed"] is None: - raise HiveApiError - - self.data.alarm = api_resp_d["parsed"] - - async def getCamera(self, device): - """Get camera data. - - Raises: - HTTPException: HTTP error has occurred updating the devices. - HiveApiError: An API error code has been returned. - """ - cameraImage = None - cameraRecording = None - hasCameraImage = False - hasCameraRecording = False - - if self.config.file: - cameraImage = self.openFile("camera.json") - cameraRecording = self.openFile("camera.json") - elif self.tokens is not None: - cameraImage = await self.api.getCameraImage(device) - hasCameraRecording = bool( - cameraImage["parsed"]["events"][0]["hasRecording"] - ) - if hasCameraRecording: - cameraRecording = await self.api.getCameraRecording( - device, cameraImage["parsed"]["events"][0]["eventId"] - ) - - if operator.contains(str(cameraImage["original"]), "20") is False: - raise HTTPException - elif cameraImage["parsed"] is None: - raise HiveApiError - else: - raise NoApiToken - - hasCameraImage = bool(cameraImage["parsed"]["events"][0]) - - self.data.camera[device["id"]] = {} - self.data.camera[device["id"]]["cameraImage"] = None - self.data.camera[device["id"]]["cameraRecording"] = None - - if cameraImage is not None and hasCameraImage: - self.data.camera[device["id"]] = {} - self.data.camera[device["id"]]["cameraImage"] = cameraImage["parsed"][ - "events" - ][0] - if cameraRecording is not None and hasCameraRecording: - self.data.camera[device["id"]]["cameraRecording"] = cameraRecording[ - "parsed" - ] - async def getDevices(self, n_id: str): """Get latest data for Hive nodes. @@ -629,22 +559,18 @@ async def getDevices(self, n_id: str): for hiveType in api_resp_p: if hiveType == "user": self.data.user = api_resp_p[hiveType] - self.config.userID = api_resp_p[hiveType]["id"] + self.config.user_id = api_resp_p[hiveType]["id"] if hiveType == "products": for aProduct in api_resp_p[hiveType]: tmpProducts.update({aProduct["id"]: aProduct}) if hiveType == "devices": for aDevice in api_resp_p[hiveType]: tmpDevices.update({aDevice["id"]: aDevice}) - if aDevice["type"] == "siren": - self.config.alarm = True - # if aDevice["type"] == "hivecamera": - # await self.getCamera(aDevice) if hiveType == "actions": 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"] + self.config.home_id = api_resp_p[hiveType]["homes"][0]["id"] _LOGGER.debug( "API returned %d products, %d devices, %d actions.", @@ -657,18 +583,16 @@ async def getDevices(self, n_id: str): if len(tmpDevices) > 0: self.data.devices = copy.deepcopy(tmpDevices) self.data.actions = copy.deepcopy(tmpActions) - if self.config.alarm: - await self.getAlarm() - self.config.lastUpdate = datetime.now() + self.config.last_updated = datetime.now() get_nodes_successful = True except HiveReauthRequired: _LOGGER.error("Reauthentication required, propagating to caller.") - self.config.lastUpdate = datetime.now() + self.config.last_update = datetime.now() raise except asyncio.TimeoutError: _LOGGER.warning("Hive API request timed out — keeping cached device data.") self._lastPollSlow = True - self.config.lastUpdate = ( + self.config.last_update = ( datetime.now() - self.config.scanInterval + timedelta(seconds=30) ) get_nodes_successful = False @@ -680,7 +604,7 @@ async def getDevices(self, n_id: str): HTTPException, ) as err: _LOGGER.error("Failed to fetch devices: %s", err) - self.config.lastUpdate = ( + self.config.last_update = ( datetime.now() - self.config.scanInterval + timedelta(seconds=30) ) get_nodes_successful = False @@ -705,7 +629,7 @@ async def startSession(self, config: dict = None): _LOGGER.debug("Starting Hive session.") await self.useFile(config.get("username", self.config.username)) await self.updateInterval( - config.get("options", {}).get("scan_interval", self.config.scanInterval) + config.get("options", {}).get("scan_interval", self.config.scan_interval) ) if config != {}: @@ -745,69 +669,68 @@ async def createDevices(self): Returns: list: List of devices """ - self.deviceList["parent"] = [] - self.deviceList["alarm_control_panel"] = [] - self.deviceList["binary_sensor"] = [] - self.deviceList["camera"] = [] - self.deviceList["climate"] = [] - self.deviceList["light"] = [] - self.deviceList["sensor"] = [] - self.deviceList["switch"] = [] - self.deviceList["water_heater"] = [] + self.device_list["parent_device"] = [] + self.device_list["binary_sensor"] = [] + self.device_list["climate"] = [] + self.device_list["light"] = [] + self.device_list["sensor"] = [] + self.device_list["switch"] = [] + self.device_list["water_heater"] = [] hive_type = HIVE_TYPES["Thermo"] + HIVE_TYPES["Sensor"] - for aDevice in self.data["devices"]: - if self.data["devices"][aDevice]["type"] == "hub": - self.hub_id = aDevice + for device_id, device_data in self.data["devices"].items(): + if device_data["type"] == "hub": + self.hub_id = device_id break - for aDevice in self.data["devices"]: - d = self.data.devices[aDevice] - device_list = DEVICES.get(self.data.devices[aDevice]["type"], []) - for code in device_list: - eval("self." + code) - if self.data["devices"][aDevice]["type"] in hive_type: - self.config.battery.append(d["id"]) + for device_id, device_data in self.data["devices"].items(): + entity_configs = DEVICES.get(device_data["type"], []) + + for config in entity_configs: + kwargs = self.helper._build_kwargs(config) + self.addList(config.entity_type, device_data, **kwargs) - if "action" in HIVE_TYPES["Switch"]: - for action in self.data["actions"]: - a = self.data["actions"][action] # noqa: F841 - eval("self." + ACTIONS) + if device_data["type"] in hive_type: + self.config.battery.append(device_data["id"]) hive_type = HIVE_TYPES["Heating"] + HIVE_TYPES["Switch"] + HIVE_TYPES["Light"] - for aProduct in self.data.products: - p = self.data.products[aProduct] - if "error" in p: + for product_id, product_data in self.data.products.items(): + if "error" in product_data: continue - # Only consider single items or heating groups + + # Skip non-heating groups if ( - p.get("isGroup", False) - and self.data.products[aProduct]["type"] not in HIVE_TYPES["Heating"] + product_data.get("isGroup", False) + and product_data["type"] not in HIVE_TYPES["Heating"] ): continue - product_list = PRODUCTS.get(self.data.products[aProduct]["type"], []) - product_name = self.data.products[aProduct]["state"].get("name", "Unknown") - for code in product_list: + + entity_configs = PRODUCTS.get(product_data["type"], []) + + for config in entity_configs: + kwargs = self.helper._build_kwargs(config) + try: - eval("self." + code) + self.addList(config.entity_type, product_data, **kwargs) except (NameError, AttributeError) as e: + product_name = product_data["state"].get("name", "Unknown") _LOGGER.warning(f"Device {product_name} cannot be setup - {e}") - if self.data.products[aProduct]["type"] in hive_type: - self.config.mode.append(p["id"]) + if product_data["type"] in hive_type: + self.config.mode.append(product_data["id"]) _LOGGER.debug( - "Device discovery found: %d parent, %d binary_sensor, %d climate, %d light, %d sensor, %d switch, %d water_heater", - len(self.deviceList.get("parent", [])), - len(self.deviceList.get("binary_sensor", [])), - len(self.deviceList.get("climate", [])), - len(self.deviceList.get("light", [])), - len(self.deviceList.get("sensor", [])), - len(self.deviceList.get("switch", [])), - len(self.deviceList.get("water_heater", [])), + "Device discovery found: %d parent_device, %d binary_sensor, %d climate, %d light, %d sensor, %d switch, %d water_heater", + len(self.device_list.get("parent_device", [])), + len(self.device_list.get("binary_sensor", [])), + len(self.device_list.get("climate", [])), + len(self.device_list.get("light", [])), + len(self.device_list.get("sensor", [])), + len(self.device_list.get("switch", [])), + len(self.device_list.get("water_heater", [])), ) - return self.deviceList + return self.device_list @staticmethod def epochTime(date_time: any, pattern: str, action: str):