''' Verisure session, using verisure app api ''' import json import logging import os import pickle import requests LOGGER = logging.getLogger(__package__) class Error(Exception): ''' Verisure session error ''' class RequestError(Error): ''' Request ''' class LoginError(Error): ''' Login failed ''' class LogoutError(Error): ''' Logout failed ''' class ResponseError(Error): ''' Unexcpected response ''' def __init__(self, status_code, text): super().__init__( f'Invalid response, status code: {status_code} - Data: {text}') def query_func(f): """A wrapper that indicates that the function is a query (used by CLI)""" f.is_query = True return f class VariableTypes: """Types for query parameters""" class DeviceLabel(str): """Device label""" class TransactionId(str): """Transaction ID""" class RequestId(str): """Request ID""" class ArmFutureState(str): """Arm state""" # ARMED_AWAY, DISARMED, ARMED_HOME class LockFutureState(str): """Lock state""" class Code(str): """Code""" class Giid(str): """Giid""" class Session(object): """ Verisure app session Args: username (str): Username used to login to verisure app password (str): Password used to login to verisure app cookie_file_name (str): path to cookie file """ def __init__(self, username, password, cookie_file_name='~/.verisure-cookie'): LOGGER.info(f"Initialize Session ({username=}, {cookie_file_name=})") self._username = username self._password = password self._cookies = None self._cookie_file_name = os.path.expanduser(cookie_file_name) self._trust_token = None self._giid = None self._base_url = None self._base_urls = ['https://automation01.verisure.com', 'https://automation02.verisure.com'] self._post = self._wrap_request(requests.post) self._delete = self._wrap_request(requests.delete) self._get = self._wrap_request(requests.get) def _wrap_request(self, function): """ Used to wrap methods from the requests module to try both urls and remember the last working one. """ def wrapper(url, *args, **kwargs): last_exception = Error("Unknown error") base_urls = self._base_urls.copy() for base_url in base_urls: try: response = function(base_url+url, *args, **kwargs) if response.status_code > 200 or "errors" in response.text: LOGGER.debug( f"{response.request.method} {response.request.url} " f"{response.status_code} f{response.text}" ) if response.status_code >= 500: last_exception = ResponseError(response.status_code, response.text) self._base_urls.reverse() continue if response.status_code >= 400: last_exception = LoginError(response.text) break if response.status_code == 200: if "SYS_00004" in response.text: self._base_urls.reverse() continue return response except requests.exceptions.RequestException as ex: LOGGER.warning(f"Unexpected error on '{base_url}{url}' ({ex=})") last_exception = RequestError(str(ex)) self._base_urls.reverse() raise last_exception return wrapper def login(self): """ Login to verisure app api Login before calling any read or write commands Return installations """ response = self._post( "/auth/login", headers={'APPLICATION_ID': 'PS_PYTHON'}, auth=(self._username, self._password)) if "stepUpToken" in response.text: raise LoginError("Multifactor authentication enabled, " "disable or create MFA cookie") self._cookies = response.cookies with open(self._cookie_file_name, 'wb') as f: pickle.dump(self._cookies, f) installations = self.get_installations() if 'errors' not in installations: return installations raise LoginError("Failed to log in") def request_mfa(self): """ Request MFA verification code """ response = self._post( url="/auth/login", headers={'APPLICATION_ID': 'PS_PYTHON'}, auth=(self._username, self._password)) if "stepUpToken" not in response.text: raise LoginError("Multifactor authentication disabled, " "use regular login instead") self._cookies = response.cookies for mfa_type in ['phone', 'email']: try: mfa_response = self._post( url=f"/auth/mfa?type={mfa_type}", headers={'APPLICATION_ID': 'PS_PYTHON'}, cookies=self._cookies) if mfa_response.status_code == 200: return except Exception as ex: raise LoginError("Failed to request MFA type") from ex raise LoginError("Failed to log in") def validate_mfa(self, code): """ Validate mfa request Return installations """ response = self._post( url="/auth/mfa/validate", headers={ 'APPLICATION_ID': 'PS_PYTHON', 'Accept': 'application/json', 'Content-Type': 'application/json'}, cookies=self._cookies, data=json.dumps({"token": code})) self._cookies = response.cookies trust_response = self._post( url="/auth/trust", headers={ 'APPLICATION_ID': 'PS_PYTHON', 'Accept': 'application/json', }, cookies=self._cookies) self._cookies.update(trust_response.cookies) with open(self._cookie_file_name, 'wb') as cookie_file: pickle.dump(self._cookies, cookie_file) self._trust_token = trust_response.json() installations = self.get_installations() if 'errors' not in installations: return installations raise LoginError("Failed to log in") def login_cookie(self): """ Login using cookie Return installations """ # Load cookie from file try: with open(self._cookie_file_name, 'rb') as cookie_file: self._cookies = pickle.load(cookie_file) except Exception as ex: raise LoginError("Failed to read cookie") from ex # Login cookie_jar = requests.sessions.RequestsCookieJar() for name, value in self._cookies.items(): if 'vs-trust' in name: cookie_jar.set(name, value) response = self._post( url="/auth/login", headers={'APPLICATION_ID': 'PS_PYTHON'}, auth=(self._username, self._password), cookies=cookie_jar) self._cookies.update(response.cookies) with open(self._cookie_file_name, 'wb') as f: pickle.dump(self._cookies, f) installations = self.get_installations() if 'errors' not in installations: return installations raise LoginError("Failed to log in") def update_cookie(self): """ Update expired cookie Cookie can last 15 minutes before it needs to be updated. """ cookie_jar = requests.sessions.RequestsCookieJar() if self._cookies is not None: for name, value in self._cookies.items(): if name in ['vid', 'vs-refresh']: cookie_jar[name] = value response = self._get( url="/auth/token", headers={'APPLICATION_ID': 'PS_PYTHON'}, cookies=cookie_jar) self._cookies.update(response.cookies) with open(self._cookie_file_name, 'wb') as cookie_file: pickle.dump(self._cookies, cookie_file) LOGGER.debug(f"Saved cookies: {[cookie for cookie in self._cookies.keys()]}") def logout(self): """ Log out from the verisure app api """ try: if self._trust_token is not None: token = self._trust_token['trustTokenValue'] self._delete( url=f"/auth/trust/{token}", headers={ 'APPLICATION_ID': 'PS_PYTHON', 'Accept': 'application/json', }, cookies=self._cookies) self._delete( url="/auth/logout", headers={'APPLICATION_ID': 'PS_PYTHON'}, cookies=self._cookies) finally: self._base_url = None self._giid = None self._cookies = None self._trust_token = None if os.path.exists(self._cookie_file_name): os.remove(self._cookie_file_name) def request(self, *operations): """Request operations""" if not operations: # Return empty json if no operations were requested return json.loads("{}") response = self._post( '/graphql', headers={ 'APPLICATION_ID': 'PS_PYTHON', 'Accept': 'application/json'}, cookies=self._cookies, data=json.dumps(list(operations))) return json.loads(response.text) def get_installations(self): """ Get information about installations """ return self.request(self.fetch_all_installations()) def set_giid(self, giid): """ Set installation giid Args: giid (str): Installation identifier """ self._giid = giid LOGGER.info(f"Installation identifier set ({giid=})") @query_func def arm_away(self, code: VariableTypes.Code, giid: VariableTypes.Giid=None): """Set arm status away""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "armAway", "variables": { "giid": giid or self._giid, "code": code}, "query": "mutation armAway($giid: String!, $code: String!) {\n armStateArmAway(giid: $giid, code: $code)\n}\n", # noqa: E501 } @query_func def arm_home(self, code: VariableTypes.Code, giid: VariableTypes.Giid=None): """Set arm state home""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "armHome", "variables": { "giid": giid or self._giid, "code": code}, "query": "mutation armHome($giid: String!, $code: String!) {\n armStateArmHome(giid: $giid, code: $code)\n}\n", # noqa: E501 } @query_func def arm_state(self, giid: VariableTypes.Giid=None): """Read arm state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "ArmState", "variables": { "giid": giid or self._giid}, "query": "query ArmState($giid: String!) {\n installation(giid: $giid) {\n armState {\n type\n statusType\n date\n name\n changedVia\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def broadband(self, giid: VariableTypes.Giid=None): """Get broadband status""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Broadband", "variables": { "giid": giid or self._giid}, "query": "query Broadband($giid: String!) {\n installation(giid: $giid) {\n broadband {\n testDate\n isBroadbandConnected\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def capability(self, giid: VariableTypes.Giid=None): """Get capability""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Capability", "variables": { "giid": giid or self._giid}, "query": "query Capability($giid: String!) {\n installation(giid: $giid) {\n capability {\n current\n gained {\n capability\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def charge_sms(self, giid: VariableTypes.Giid=None): """Charge SMS""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "ChargeSms", "variables": { "giid": giid or self._giid}, "query": "query ChargeSms($giid: String!) {\n installation(giid: $giid) {\n chargeSms {\n chargeSmartPlugOnOff\n chargeLockUnlock\n chargeArmDisarm\n chargeNotifications\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def climate(self, giid: VariableTypes.Giid=None): """Get climate""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Climate", "variables": { "giid": giid or self._giid}, "query": "query Climate($giid: String!) {\n installation(giid: $giid) {\n climates {\n device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n __typename\n }\n humidityEnabled\n humidityTimestamp\n humidityValue\n temperatureTimestamp\n temperatureValue\n thresholds {\n aboveMaxAlert\n belowMinAlert\n sensorType\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def disarm(self, code: VariableTypes.Code, giid: VariableTypes.Giid=None): """Disarm alarm""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "disarm", "variables": { "giid": giid or self._giid, "code": code}, "query": "mutation disarm($giid: String!, $code: String!) {\n armStateDisarm(giid: $giid, code: $code)\n}\n", # noqa: E501 } @query_func def door_lock(self, device_label: VariableTypes.DeviceLabel, code: VariableTypes.Code, giid: VariableTypes.Giid=None): """Lock door""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "DoorLock", "variables": { "giid": giid or self._giid, "deviceLabel": device_label, "input": { "code": code, }, }, "query": "mutation DoorLock($giid: String!, $deviceLabel: String!, $input: LockDoorInput!) {\n DoorLock(giid: $giid, deviceLabel: $deviceLabel, input: $input)\n}\n", # noqa: E501 } @query_func def door_lock_configuration(self, device_label: VariableTypes.DeviceLabel, giid: VariableTypes.Giid=None): """Get door lock configuration""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "DoorLockConfiguration", "variables": { "giid": giid or self._giid, "deviceLabel": device_label}, "query": "query DoorLockConfiguration($giid: String!, $deviceLabel: String!) {\n installation(giid: $giid) {\n smartLocks(filter: {deviceLabels: [$deviceLabel]}) {\n device {\n area\n deviceLabel\n __typename\n }\n configuration {\n ... on YaleLockConfiguration {\n autoLockEnabled\n voiceLevel\n volume\n __typename\n }\n ... on DanaLockConfiguration {\n holdBackLatchDuration\n twistAssistEnabled\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def set_autolock_enabled(self, device_label: VariableTypes.DeviceLabel, auto_lock_enabled: bool, giid: VariableTypes.Giid=None): """Enable or disable autolock""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "DoorLockUpdateConfig", "variables": { "giid": giid or self._giid, "deviceLabel": device_label, "input": { "autoLockEnabled": auto_lock_enabled } }, "query": "mutation DoorLockUpdateConfig($giid: String!, $deviceLabel: String!, $input: DoorLockUpdateConfigInput!) {\n DoorLockUpdateConfig(giid: $giid, deviceLabel: $deviceLabel, input: $input)\n}\n", # noqa: E501 } @query_func def door_unlock(self, device_label: VariableTypes.DeviceLabel, code: VariableTypes.Code, giid: VariableTypes.Giid=None): """Unlock door""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "DoorUnlock", "variables": { "giid": giid or self._giid, "deviceLabel": device_label, "input": { "code": code, }, }, "query": "mutation DoorUnlock($giid: String!, $deviceLabel: String!, $input: LockDoorInput!) {\n DoorUnlock(giid: $giid, deviceLabel: $deviceLabel, input: $input)\n}\n", # noqa: E501 } @query_func def door_window(self, giid: VariableTypes.Giid=None): """Read status of door and window sensors""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "DoorWindow", "variables": { "giid": giid or self._giid}, "query": "query DoorWindow($giid: String!) {\n installation(giid: $giid) {\n doorWindows {\n device {\n deviceLabel\n __typename\n }\n type\n area\n state\n wired\n reportTime\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def event_log(self, giid: VariableTypes.Giid=None): """Read event log""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "EventLog", "variables": { "giid": giid or self._giid, "offset": 0, "pagesize": 15, "eventCategories": ["INTRUSION", "FIRE", "SOS", "WATER", "ANIMAL", "TECHNICAL", "WARNING", "ARM", "DISARM", "LOCK", "UNLOCK", "PICTURE", "CLIMATE", "CAMERA_SETTINGS"], # noqa: E501 "eventContactIds": [], "eventDeviceLabels": [], "fromDate": None, "toDate": None }, "query": "query EventLog($giid: String!, $offset: Int!, $pagesize: Int!, $eventCategories: [String], $fromDate: String, $toDate: String, $eventContactIds: [String], $eventDeviceLabels: [String]) {\n installation(giid: $giid) {\n eventLog(offset: $offset, pagesize: $pagesize, eventCategories: $eventCategories, eventContactIds: $eventContactIds, eventDeviceLabels: $eventDeviceLabels, fromDate: $fromDate, toDate: $toDate) {\n moreDataAvailable\n pagedList {\n device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n __typename\n }\n arloDevice {\n name\n __typename\n }\n gatewayArea\n eventType\n eventCategory\n eventSource\n eventId\n eventTime\n userName\n armState\n userType\n climateValue\n sensorType\n eventCount\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def fetch_all_installations(self): """Fetch installations""" return { "operationName": "fetchAllInstallations", "variables": { "email": self._username}, "query": "query fetchAllInstallations($email: String!){\n account(email: $email) {\n installations {\n giid\n alias\n customerType\n dealerId\n subsidiary\n pinCodeLength\n locale\n address {\n street\n city\n postalNumber\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def firmware(self, giid: VariableTypes.Giid=None): """Get firmware information""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Firmware", "variables": { "giid": giid or self._giid }, "query": "query Firmware($giid: String!) {\n installation(giid: $giid) {\n firmware {\n status {\n latestFirmware\n requestedFirmware\n upgradeable\n status\n gateways {\n reportedRunningFirmware\n deviceLabel\n status\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n" # noqa: E501 } @query_func def guardian_sos(self): """Guardian SOS""" return { "operationName": "GuardianSos", "variables": {}, "query": "query GuardianSos {\n guardianSos {\n serverTime\n sos {\n fullName\n phone\n deviceId\n deviceName\n giid\n type\n username\n expireDate\n warnBeforeExpireDate\n contactId\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def is_guardian_activated(self, giid: VariableTypes.Giid=None): """Is guardian activated""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "IsGuardianActivated", "variables": { "giid": giid or self._giid, "featureName": "GUARDIAN"}, "query": "query IsGuardianActivated($giid: String!, $featureName: String!) {\n installation(giid: $giid) {\n activatedFeature {\n isFeatureActivated(featureName: $featureName)\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def permissions(self, giid: VariableTypes.Giid=None): """Permissions""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Permissions", "variables": { "giid": giid or self._giid, "email": self._username}, "query": "query Permissions($giid: String!, $email: String!) {\n permissions(giid: $giid, email: $email) {\n accountPermissionsHash\n name\n __typename\n }\n}\n", # noqa: E501 } @query_func def poll_arm_state(self, transaction_id: VariableTypes.TransactionId, future_state: VariableTypes.ArmFutureState, giid: VariableTypes.Giid=None): """Poll arm state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "pollArmState", "variables": { "giid": giid or self._giid, "transactionId": transaction_id, "futureState": future_state}, "query": "query pollArmState($giid: String!, $transactionId: String, $futureState: ArmStateStatusTypes!) {\n installation(giid: $giid) {\n armStateChangePollResult(transactionId: $transactionId, futureState: $futureState) {\n result\n createTime\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def poll_lock_state(self, transaction_id: VariableTypes.TransactionId, device_label: VariableTypes.DeviceLabel, future_state: VariableTypes.LockFutureState, giid: VariableTypes.Giid=None): """Poll lock state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "pollLockState", "variables": { "giid": giid or self._giid, "transactionId": transaction_id, "deviceLabel": device_label, "futureState": future_state}, "query": "query pollLockState($giid: String!, $transactionId: String, $deviceLabel: String!, $futureState: DoorLockState!) {\n installation(giid: $giid) {\n doorLockStateChangePollResult(transactionId: $transactionId, deviceLabel: $deviceLabel, futureState: $futureState) {\n result\n createTime\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def remaining_sms(self, giid: VariableTypes.Giid=None): """Get remaing number of SMS""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "RemainingSms", "variables": { "giid": giid or self._giid}, "query": "query RemainingSms($giid: String!) {\n installation(giid: $giid) {\n remainingSms\n __typename\n }\n}\n", # noqa: E501 } @query_func def smart_button(self, giid: VariableTypes.Giid=None): """Get smart button state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "SmartButton", "variables": { "giid": giid or self._giid}, "query": "query SmartButton($giid: String!) {\n installation(giid: $giid) {\n smartButton {\n entries {\n smartButtonId\n icon\n label\n color\n active\n action {\n actionType\n expectedState\n target {\n ... on Installation {\n alias\n __typename\n }\n ... on Device {\n deviceLabel\n area\n gui {\n label\n __typename\n }\n featureStatuses(type: \"SmartPlug\") {\n device {\n deviceLabel\n __typename\n }\n ... on SmartPlug {\n icon\n isHazardous\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def smart_lock(self, giid: VariableTypes.Giid=None): """Get smart lock state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "SmartLock", "variables": { "giid": giid or self._giid}, "query": "query SmartLock($giid: String!) {\n installation(giid: $giid) {\n smartLocks {\n lockStatus\n doorState\n lockMethod\n eventTime\n doorLockType\n secureMode\n device {\n deviceLabel\n area\n __typename\n }\n user {\n name\n __typename\n }\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def set_smartplug(self, device_label: VariableTypes.DeviceLabel, state: bool, giid: VariableTypes.Giid=None): """Set state of smart plug""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "UpdateState", "variables": { "giid": giid or self._giid, "deviceLabel": device_label, "state": state}, "query": "mutation UpdateState($giid: String!, $deviceLabel: String!, $state: Boolean!) {\n SmartPlugSetState(giid: $giid, input: [{deviceLabel: $deviceLabel, state: $state}])}", # noqa: E501 } @query_func def smartplug(self, device_label: VariableTypes.DeviceLabel, giid: VariableTypes.Giid=None): """Read status of a single smart plug""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "SmartPlug", "variables": { "giid": giid or self._giid, "deviceLabel": device_label}, "query": "query SmartPlug($giid: String!, $deviceLabel: String!) {\n installation(giid: $giid) {\n smartplugs(filter: {deviceLabels: [$deviceLabel]}) {\n device {\n deviceLabel\n area\n __typename\n }\n currentState\n icon\n isHazardous\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def smartplugs(self, giid: VariableTypes.Giid=None): """Read status of all smart plugs""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "SmartPlug", "variables": { "giid": giid or self._giid}, "query": "query SmartPlug($giid: String!) {\n installation(giid: $giid) {\n smartplugs {\n device {\n deviceLabel\n area\n __typename\n }\n currentState\n icon\n isHazardous\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def user_trackings(self, giid: VariableTypes.Giid=None): """Read user tracking status""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "userTrackings", "variables": { "giid": giid or self._giid}, "query": "query userTrackings($giid: String!) {\n installation(giid: $giid) {\n userTrackings {\n isCallingUser\n webAccount\n status\n xbnContactId\n currentLocationName\n deviceId\n name\n initials\n currentLocationTimestamp\n deviceName\n currentLocationId\n __typename\n }\n __typename\n }\n}\n", # noqa: E501 } @query_func def cameras(self, giid: VariableTypes.Giid=None): """Get cameras state""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "Camera", "variables": { "all": True, "giid": giid or self._giid}, "query": "query Camera($giid: String!, $all: Boolean!) {\n installation(giid: $giid) {\n cameras(allCameras: $all) {\n visibleOnCard\n initiallyConfigured\n imageCaptureAllowed\n imageCaptureAllowedByArmstate\n device {\n deviceLabel\n area\n __typename\n }\n latestCameraSeries {\n image {\n imageId\n imageStatus\n captureTime\n url\n }\n }\n }\n }\n}", # noqa: E501 } @query_func def cameras_last_image(self, giid: VariableTypes.Giid=None): """Get cameras last image""" assert giid or self._giid, "Set default giid or pass explicit" return { "variables": { "giid": giid or self._giid}, "query": "query queryCaptureImageRequestStatus($giid: String!) {\n installation(giid: $giid) {\n cameraContentProvider {\n latestImage {\n deviceLabel\n mediaId\n contentType\n contentUrl\n timestamp\n duration\n thumbnailUrl\n bitRate\n width\n height\n codec\n }\n }\n }\n}", # noqa: E501 } @query_func def cameras_image_series(self, limit=50, offset=0, giid: VariableTypes.Giid=None): """Get the cameras image series""" assert giid or self._giid, "Set default giid or pass explicit" return { "operationName": "GQL_CCCP_SearchMedia", "variables": { "giid": giid or self._giid, "limit": limit, "offset": offset}, "query": "mutation GQL_CCCP_SearchMedia(\n $giid: BigInt!\n $offset: Int\n $limit: Int\n $fromDate: Date\n $toDate: Date) {\n\n ContentProviderMediaSearch(\n giid: $giid\n offset: $offset\n limit: $limit\n fromDate: $fromDate\n toDate: $toDate\n ) {\n totalNumberOfMediaSeries\n mediaSeriesList {\n seriesId\n storageType\n viewed\n timestamp\n deviceMediaList {\n contentUrl\n mediaAvailable\n deviceLabel\n mediaId\n contentType\n timestamp\n requestTimestamp\n duration\n expiryDate\n viewed\n thumbnailUrl\n bitRate\n width\n height\n codec\n }\n }\n }\n}", # noqa: E501} } @query_func def camera_get_request_id(self, device_label: VariableTypes.DeviceLabel, giid: VariableTypes.Giid=None): """Get requestId for camera_capture""" assert giid or self._giid, "Set default giid or pass explicit" return { "variables": { "deviceIdentifier": "RandomString", "deviceLabel": device_label, "giid": giid or self._giid, "resolution": "high"}, "query": "mutation cccp($giid: String!, $deviceLabel: String!, $resolution: String!, $deviceIdentifier: String) {\n ContentProviderCaptureImageRequest(giid: $giid, deviceLabel: $deviceLabel, resolution: $resolution, deviceIdentifier: $deviceIdentifier) {\n requestId\n }\n}", # noqa: E501 } @query_func def camera_capture(self, device_label: VariableTypes.DeviceLabel, request_id: VariableTypes.RequestId, giid: VariableTypes.Giid=None): """Capture a new image from a camera""" assert giid or self._giid, "Set default giid or pass explicit" return { "variables": { "deviceLabel": device_label, "giid": giid or self._giid, "requestId": request_id}, "query": "query queryCaptureImageRequestStatus($giid: String!, $deviceLabel: String!, $requestId: BigInt!) {\n installation(giid: $giid) {\n cameraContentProvider {\n captureImageRequestStatus(deviceLabel: $deviceLabel, requestId: $requestId) {\n mediaRequestStatus\n }\n }\n }\n}", # noqa: E501 } def download_image(self, image_url, file_name): """Download image from url""" try: response = requests.get(image_url, stream=True) except requests.exceptions.RequestException as ex: raise RequestError("Failed to get image") from ex with open(file_name, 'wb') as image_file: for chunk in response.iter_content(chunk_size=1024): if chunk: image_file.write(chunk)