wyzeapy.services.base_service API documentation

# wyzeapy.services.base_service

View Source

      1#  Copyright (c) 2021. Mulliken, LLC - All Rights Reserved
      2#  You may use, distribute and modify this code under the terms
      3#  of the attached license. You should have received a copy of
      4#  the license with this file. If not, please write to:
      5#  katie@mulliken.net to receive a copy
      6import asyncio
      7import json
      8import logging
      9import time
     10from typing import List, Tuple, Any, Dict, Optional
     11
     12import aiohttp
     13
     14from .update_manager import DeviceUpdater, UpdateManager
     15from ..const import PHONE_SYSTEM_TYPE, APP_VERSION, APP_VER, PHONE_ID, APP_NAME, OLIVE_APP_ID, APP_INFO, SC, SV, APP_PLATFORM, SOURCE
     16from ..crypto import olive_create_signature
     17from ..payload_factory import olive_create_hms_patch_payload, olive_create_hms_payload, \
     18    olive_create_hms_get_payload, ford_create_payload, olive_create_get_payload, olive_create_post_payload, \
     19    olive_create_user_info_payload, devicemgmt_create_capabilities_payload, devicemgmt_get_iot_props_list
     20from ..types import PropertyIDs, Device, DeviceMgmtToggleType
     21from ..utils import check_for_errors_standard, check_for_errors_hms, check_for_errors_lock, \
     22    check_for_errors_iot, wyze_encrypt, check_for_errors_devicemgmt
     23from ..wyze_auth_lib import WyzeAuthLib
     24
     25_LOGGER = logging.getLogger(__name__)
     26
     27
     28class BaseService:
     29    """Base service class for interacting with Wyze devices."""
     30    _devices: Optional[List[Device]] = None
     31    _last_updated_time: time = 0  # preload a value of 0 so that comparison will succeed on the first run
     32    _min_update_time = 1200  # lets let the device_params update every 20 minutes for now. This could probably reduced signicficantly.
     33    _update_lock: asyncio.Lock = asyncio.Lock() # fmt: skip
     34    _update_manager: UpdateManager = UpdateManager()
     35    _update_loop = None
     36    _updater: DeviceUpdater = None
     37    _updater_dict = {}
     38
     39    def __init__(self, auth_lib: WyzeAuthLib):
     40        """Initialize the base service."""
     41        self._auth_lib = auth_lib
     42
     43    @staticmethod
     44    async def start_update_manager():
     45        """Start the update manager."""
     46        if BaseService._update_loop is None:
     47            BaseService._update_loop = asyncio.get_event_loop()
     48            BaseService._update_loop.create_task(BaseService._update_manager.update_next())
     49
     50    def register_updater(self, device: Device, interval):
     51        """
     52        Register a device to be updated at a specific interval.
     53
     54        Parameters
     55        ----------
     56        device : Device
     57            The device to register.
     58        interval : int
     59            The interval in seconds.
     60        """
     61        self._updater = DeviceUpdater(self, device, interval)
     62        BaseService._update_manager.add_updater(self._updater)
     63        self._updater_dict[self._updater.device] = self._updater
     64
     65    def unregister_updater(self, device: Device):
     66        if self._updater:
     67            BaseService._update_manager.del_updater(self._updater_dict[device])
     68            del self._updater_dict[device]
     69
     70    async def set_push_info(self, on: bool) -> None:
     71        await self._auth_lib.refresh_if_should()
     72
     73        url = "https://api.wyzecam.com/app/user/set_push_info"
     74        payload = {
     75            "phone_system_type": PHONE_SYSTEM_TYPE,
     76            "app_version": APP_VERSION,
     77            "app_ver": APP_VER,
     78            "push_switch": "1" if on else "2",
     79            "sc": SC,
     80            "ts": int(time.time()),
     81            "sv": SV,
     82            "access_token": self._auth_lib.token.access_token,
     83            "phone_id": PHONE_ID,
     84            "app_name": APP_NAME
     85        }
     86
     87        response_json = await self._auth_lib.post(url, json=payload)
     88
     89        check_for_errors_standard(self, response_json)
     90
     91    async def get_user_profile(self) -> Dict[Any, Any]:
     92        await self._auth_lib.refresh_if_should()
     93
     94        payload = olive_create_user_info_payload()
     95        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
     96        headers = {
     97            'Accept-Encoding': 'gzip',
     98            'User-Agent': 'myapp',
     99            'appid': OLIVE_APP_ID,
    100            'appinfo': APP_INFO,
    101            'phoneid': PHONE_ID,
    102            'access_token': self._auth_lib.token.access_token,
    103            'signature2': signature
    104        }
    105
    106        url = 'https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile'
    107
    108        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    109
    110        return response_json
    111
    112    async def get_object_list(self) -> List[Device]:
    113        """
    114        Wraps the api.wyzecam.com/app/v2/home_page/get_object_list endpoint
    115
    116        :return: List of devices
    117        """
    118        await self._auth_lib.refresh_if_should()
    119
    120        payload = {
    121            "phone_system_type": PHONE_SYSTEM_TYPE,
    122            "app_version": APP_VERSION,
    123            "app_ver": APP_VER,
    124            "sc": "9f275790cab94a72bd206c8876429f3c",
    125            "ts": int(time.time()),
    126            "sv": "9d74946e652647e9b6c9d59326aef104",
    127            "access_token": self._auth_lib.token.access_token,
    128            "phone_id": PHONE_ID,
    129            "app_name": APP_NAME
    130        }
    131
    132        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/home_page/get_object_list",
    133                                                  json=payload)
    134
    135        check_for_errors_standard(self, response_json)
    136        # Cache the devices so that update calls can pull more recent device_params
    137        BaseService._devices = [Device(device) for device in response_json['data']['device_list']]
    138
    139        return BaseService._devices
    140
    141    async def get_updated_params(self, device_mac: str = None) -> Dict[str, Optional[Any]]:
    142        if time.time() - BaseService._last_updated_time >= BaseService._min_update_time:
    143            await self.get_object_list()
    144            BaseService._last_updated_time = time.time()
    145        ret_params = {}
    146        for dev in BaseService._devices:
    147            if dev.mac == device_mac:
    148                ret_params = dev.device_params
    149        return ret_params
    150
    151    async def _get_property_list(self, device: Device) -> List[Tuple[PropertyIDs, Any]]:
    152        """
    153        Wraps the api.wyzecam.com/app/v2/device/get_property_list endpoint
    154
    155        :param device: Device to get properties for
    156        :return: List of PropertyIDs and values
    157        """
    158
    159        await self._auth_lib.refresh_if_should()
    160
    161        payload = {
    162            "phone_system_type": PHONE_SYSTEM_TYPE,
    163            "app_version": APP_VERSION,
    164            "app_ver": APP_VER,
    165            "sc": "9f275790cab94a72bd206c8876429f3c",
    166            "ts": int(time.time()),
    167            "sv": "9d74946e652647e9b6c9d59326aef104",
    168            "access_token": self._auth_lib.token.access_token,
    169            "phone_id": PHONE_ID,
    170            "app_name": APP_NAME,
    171            "device_model": device.product_model,
    172            "device_mac": device.mac,
    173            "target_pid_list": []
    174        }
    175
    176        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_property_list",
    177                                                  json=payload)
    178
    179        check_for_errors_standard(self, response_json)
    180        properties = response_json['data']['property_list']
    181        property_list = []
    182        for prop in properties:
    183            try:
    184                property_id = PropertyIDs(prop['pid'])
    185                property_list.append((
    186                    property_id,
    187                    prop['value']
    188                ))
    189            except ValueError:
    190                pass
    191
    192        return property_list
    193
    194    async def _set_property_list(self, device: Device, plist: List[Dict[str, str]]) -> None:
    195        """
    196        Wraps the api.wyzecam.com/app/v2/device/set_property_list endpoint
    197
    198        :param device: The device for which to set the property(ies)
    199        :param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
    200        :return:
    201        """
    202
    203        await self._auth_lib.refresh_if_should()
    204
    205        payload = {
    206            "phone_system_type": PHONE_SYSTEM_TYPE,
    207            "app_version": APP_VERSION,
    208            "app_ver": APP_VER,
    209            "sc": "9f275790cab94a72bd206c8876429f3c",
    210            "ts": int(time.time()),
    211            "sv": "9d74946e652647e9b6c9d59326aef104",
    212            "access_token": self._auth_lib.token.access_token,
    213            "phone_id": PHONE_ID,
    214            "app_name": APP_NAME,
    215            "property_list": plist,
    216            "device_model": device.product_model,
    217            "device_mac": device.mac
    218        }
    219
    220        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/set_property_list",
    221                                                  json=payload)
    222
    223        check_for_errors_standard(self, response_json)
    224
    225    async def _run_action_list(self, device: Device, plist: List[Dict[Any, Any]]) -> None:
    226        """
    227        Wraps the api.wyzecam.com/app/v2/auto/run_action_list endpoint
    228
    229        :param device: The device for which to run the action list
    230        :param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
    231        """
    232        await self._auth_lib.refresh_if_should()
    233
    234        payload = {
    235            "phone_system_type": PHONE_SYSTEM_TYPE,
    236            "app_version": APP_VERSION,
    237            "app_ver": APP_VER,
    238            "sc": "9f275790cab94a72bd206c8876429f3c",
    239            "ts": int(time.time()),
    240            "sv": "9d74946e652647e9b6c9d59326aef104",
    241            "access_token": self._auth_lib.token.access_token,
    242            "phone_id": PHONE_ID,
    243            "app_name": APP_NAME,
    244            "action_list": [
    245                {
    246                    "instance_id": device.mac,
    247                    "action_params": {
    248                        "list": [
    249                            {
    250                                "mac": device.mac,
    251                                "plist": plist
    252                            }
    253                        ]
    254                    },
    255                    "provider_key": device.product_model,
    256                    "action_key": "set_mesh_property"
    257                }
    258            ]
    259        }
    260
    261        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/auto/run_action_list",
    262                                                  json=payload)
    263
    264        check_for_errors_standard(self, response_json)
    265
    266    async def _get_event_list(self, count: int) -> Dict[Any, Any]:
    267        """
    268        Wraps the api.wyzecam.com/app/v2/device/get_event_list endpoint
    269
    270        :param count: Number of events to gather
    271        :return: Response from the server
    272        """
    273
    274        await self._auth_lib.refresh_if_should()
    275
    276        payload = {
    277            "phone_id": PHONE_ID,
    278            "begin_time": int((time.time() - (60 * 60)) * 1000),
    279            "event_type": "",
    280            "app_name": APP_NAME,
    281            "count": count,
    282            "app_version": APP_VERSION,
    283            "order_by": 2,
    284            "event_value_list": [
    285                "1",
    286                "13",
    287                "10",
    288                "12"
    289            ],
    290            "sc": "9f275790cab94a72bd206c8876429f3c",
    291            "device_mac_list": [],
    292            "event_tag_list": [],
    293            "sv": "782ced6909a44d92a1f70d582bbe88be",
    294            "end_time": int(time.time() * 1000),
    295            "phone_system_type": PHONE_SYSTEM_TYPE,
    296            "app_ver": APP_VER,
    297            "ts": 1623612037763,
    298            "device_mac": "",
    299            "access_token": self._auth_lib.token.access_token
    300        }
    301
    302        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_event_list",
    303                                                  json=payload)
    304
    305        check_for_errors_standard(self, response_json)
    306        return response_json
    307
    308    async def _run_action(self, device: Device, action: str) -> None:
    309        """
    310        Wraps the api.wyzecam.com/app/v2/auto/run_action endpoint
    311
    312        :param device: The device for which to run the action
    313        :param action: The action to run
    314        :return:
    315        """
    316
    317        await self._auth_lib.refresh_if_should()
    318
    319        payload = {
    320            "phone_system_type": PHONE_SYSTEM_TYPE,
    321            "app_version": APP_VERSION,
    322            "app_ver": APP_VER,
    323            "sc": "9f275790cab94a72bd206c8876429f3c",
    324            "ts": int(time.time()),
    325            "sv": "9d74946e652647e9b6c9d59326aef104",
    326            "access_token": self._auth_lib.token.access_token,
    327            "phone_id": PHONE_ID,
    328            "app_name": APP_NAME,
    329            "provider_key": device.product_model,
    330            "instance_id": device.mac,
    331            "action_key": action,
    332            "action_params": {},
    333            "custom_string": "",
    334        }
    335
    336        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/auto/run_action",
    337                                                  json=payload)
    338
    339        check_for_errors_standard(self, response_json)
    340
    341    async def _run_action_devicemgmt(self, device: Device, type: str, value: str) -> None:
    342        """
    343        Wraps the devicemgmt-service-beta.wyze.com/device-management/api/action/run_action endpoint
    344
    345        :param device: The device for which to run the action
    346        :param state: on or off
    347        :return:
    348        """
    349
    350        await self._auth_lib.refresh_if_should()
    351
    352        capabilities = devicemgmt_create_capabilities_payload(type, value)
    353
    354        payload = {
    355            "capabilities": [
    356                capabilities
    357            ],
    358            "nonce": int(time.time() * 1000),
    359            "targetInfo": {
    360                "id": device.mac,
    361                "productModel": device.product_model,
    362                "type": "DEVICE"
    363            },
    364            "transactionId": "0a5b20591fedd4du1b93f90743ba0csd" # OG cam needs this (doesn't matter what the value is)
    365        }
    366
    367        headers = {
    368            "authorization": self._auth_lib.token.access_token,
    369        }
    370
    371        response_json = await self._auth_lib.post("https://devicemgmt-service-beta.wyze.com/device-management/api/action/run_action",
    372                                                  json=payload, headers=headers)
    373
    374        check_for_errors_iot(self, response_json)
    375
    376    async def _set_toggle(self, device: Device, toggleType: DeviceMgmtToggleType, state: str) -> None:
    377        """
    378        Wraps the ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management endpoint
    379
    380        :param device: The device for which to get the state
    381        :param toggleType: Enum for the toggle type
    382        :param state: String state to set for the toggle
    383        """
    384
    385        await self._auth_lib.refresh_if_should()
    386
    387        payload = {
    388            "data": [
    389                {
    390                    "device_firmware": "1234567890",
    391                    "device_id": device.mac,
    392                    "device_model": device.product_model,
    393                    "page_id": [
    394                        toggleType.pageId
    395                    ],
    396                    "toggle_update": [
    397                        {
    398                            "toggle_id": toggleType.toggleId,
    399                            "toggle_status": state
    400                        }
    401                    ]
    402                }
    403            ],
    404            "nonce": str(int(time.time() * 1000))
    405        }
    406
    407
    408        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    409        headers = {
    410            "access_token": self._auth_lib.token.access_token,
    411            "timestamp": str(int(time.time() * 1000)),
    412            "appid": OLIVE_APP_ID,
    413            "source": SOURCE,
    414            "signature2": signature,
    415            "appplatform": APP_PLATFORM,
    416            "appversion": APP_VERSION,
    417            "requestid": "35374158s4s313b9a2be7c057f2da5d1"
    418        }
    419
    420        response_json = await self._auth_lib.put("https://ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management",
    421                                                  json=payload, headers=headers)
    422
    423        check_for_errors_devicemgmt(self, response_json)
    424
    425    async def _get_iot_prop_devicemgmt(self, device: Device) -> Dict[str, Any]:
    426        """
    427        Wraps the devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop endpoint
    428
    429        :param device: The device for which to get the state
    430        :return:
    431        """
    432
    433        await self._auth_lib.refresh_if_should()
    434
    435        payload = {
    436            "capabilities": devicemgmt_get_iot_props_list(device.product_model),
    437            "nonce": int(time.time() * 1000),
    438            "targetInfo": {
    439                "id": device.mac,
    440                "productModel": device.product_model,
    441                "type": "DEVICE"
    442            }
    443        }
    444
    445        headers = {
    446            "authorization": self._auth_lib.token.access_token,
    447        }
    448
    449        response_json = await self._auth_lib.post("https://devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop",
    450                                                  json=payload, headers=headers)
    451
    452        check_for_errors_iot(self, response_json)
    453
    454        return response_json
    455
    456    async def _set_property(self, device: Device, pid: str, pvalue: str) -> None:
    457        """
    458        Wraps the api.wyzecam.com/app/v2/device/set_property endpoint
    459
    460        :param device: The device for which to set the property
    461        :param pid: The property id
    462        :param pvalue: The property value
    463        """
    464        await self._auth_lib.refresh_if_should()
    465
    466        payload = {
    467            "phone_system_type": PHONE_SYSTEM_TYPE,
    468            "app_version": APP_VERSION,
    469            "app_ver": APP_VER,
    470            "sc": "9f275790cab94a72bd206c8876429f3c",
    471            "ts": int(time.time()),
    472            "sv": "9d74946e652647e9b6c9d59326aef104",
    473            "access_token": self._auth_lib.token.access_token,
    474            "phone_id": PHONE_ID,
    475            "app_name": APP_NAME,
    476            "pvalue": pvalue,
    477            "pid": pid,
    478            "device_model": device.product_model,
    479            "device_mac": device.mac
    480        }
    481
    482        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/set_property",
    483                                                  json=payload)
    484
    485        check_for_errors_standard(self, response_json)
    486
    487    async def _monitoring_profile_active(self, hms_id: str, home: int, away: int) -> None:
    488        """
    489        Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/active endpoint
    490
    491        :param hms_id: The hms id
    492        :param home: 1 for home 0 for not
    493        :param away: 1 for away 0 for not
    494        :return:
    495        """
    496        await self._auth_lib.refresh_if_should()
    497
    498        url = "https://hms.api.wyze.com/api/v1/monitoring/v1/profile/active"
    499        query = olive_create_hms_patch_payload(hms_id)
    500        signature = olive_create_signature(query, self._auth_lib.token.access_token)
    501        headers = {
    502            'Accept-Encoding': 'gzip',
    503            'User-Agent': 'myapp',
    504            'appid': OLIVE_APP_ID,
    505            'appinfo': APP_INFO,
    506            'phoneid': PHONE_ID,
    507            'access_token': self._auth_lib.token.access_token,
    508            'signature2': signature,
    509            'Authorization': self._auth_lib.token.access_token
    510        }
    511        payload = [
    512            {
    513                "state": "home",
    514                "active": home
    515            },
    516            {
    517                "state": "away",
    518                "active": away
    519            }
    520        ]
    521        response_json = await self._auth_lib.patch(url, headers=headers, params=query, json=payload)
    522        check_for_errors_hms(self, response_json)
    523
    524    async def _get_plan_binding_list_by_user(self) -> Dict[Any, Any]:
    525        """
    526        Wraps the wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user endpoint
    527
    528        :return: The response to gathering the plan for the current user
    529        """
    530
    531        if self._auth_lib.should_refresh:
    532            await self._auth_lib.refresh()
    533
    534        url = "https://wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user"
    535        payload = olive_create_hms_payload()
    536        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    537        headers = {
    538            'Accept-Encoding': 'gzip',
    539            'User-Agent': 'myapp',
    540            'appid': OLIVE_APP_ID,
    541            'appinfo': APP_INFO,
    542            'phoneid': PHONE_ID,
    543            'access_token': self._auth_lib.token.access_token,
    544            'signature2': signature
    545        }
    546
    547        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    548        check_for_errors_hms(self, response_json)
    549        return response_json
    550
    551    async def _disable_reme_alarm(self, hms_id: str) -> None:
    552        """
    553        Wraps the hms.api.wyze.com/api/v1/reme-alarm endpoint
    554
    555        :param hms_id: The hms_id for the account
    556        """
    557        await self._auth_lib.refresh_if_should()
    558
    559        url = "https://hms.api.wyze.com/api/v1/reme-alarm"
    560        payload = {
    561            "hms_id": hms_id,
    562            "remediation_id": "emergency"
    563        }
    564        headers = {
    565            "Authorization": self._auth_lib.token.access_token
    566        }
    567
    568        response_json = await self._auth_lib.delete(url, headers=headers, json=payload)
    569
    570        check_for_errors_hms(self, response_json)
    571
    572    async def _monitoring_profile_state_status(self, hms_id: str) -> Dict[Any, Any]:
    573        """
    574        Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/state-status endpoint
    575
    576        :param hms_id: The hms_id
    577        :return: The response that includes the status
    578        """
    579        if self._auth_lib.should_refresh:
    580            await self._auth_lib.refresh()
    581
    582        url = "https://hms.api.wyze.com/api/v1/monitoring/v1/profile/state-status"
    583        query = olive_create_hms_get_payload(hms_id)
    584        signature = olive_create_signature(query, self._auth_lib.token.access_token)
    585        headers = {
    586            'User-Agent': 'myapp',
    587            'appid': OLIVE_APP_ID,
    588            'appinfo': APP_INFO,
    589            'phoneid': PHONE_ID,
    590            'access_token': self._auth_lib.token.access_token,
    591            'signature2': signature,
    592            'Authorization': self._auth_lib.token.access_token,
    593            'Content-Type': "application/json"
    594        }
    595
    596        response_json = await self._auth_lib.get(url, headers=headers, params=query)
    597
    598        check_for_errors_hms(self, response_json)
    599        return response_json
    600
    601    async def _lock_control(self, device: Device, action: str) -> None:
    602        await self._auth_lib.refresh_if_should()
    603
    604        url_path = "/openapi/lock/v1/control"
    605
    606        device_uuid = device.mac.split(".")[-1]
    607
    608        payload = {
    609            "uuid": device_uuid,
    610            "action": action  # "remoteLock" or "remoteUnlock"
    611        }
    612        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "post")
    613
    614        url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/control"
    615
    616        response_json = await self._auth_lib.post(url, json=payload)
    617
    618        check_for_errors_lock(self, response_json)
    619
    620    async def _get_lock_info(self, device: Device) -> Dict[str, Optional[Any]]:
    621        await self._auth_lib.refresh_if_should()
    622
    623        url_path = "/openapi/lock/v1/info"
    624
    625        device_uuid = device.mac.split(".")[-1]
    626
    627        payload = {
    628            "uuid": device_uuid,
    629            "with_keypad": "1"
    630        }
    631
    632        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "get")
    633
    634        url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/info"
    635
    636        response_json = await self._auth_lib.get(url, params=payload)
    637
    638        check_for_errors_lock(self, response_json)
    639
    640        return response_json
    641
    642    async def _get_lock_ble_token(self, device: Device) -> Dict[str, Optional[Any]]:
    643        await self._auth_lib.refresh_if_should()
    644
    645        url_path = "/openapi/lock/v1/ble/token"
    646
    647        payload = {
    648            "uuid": device.mac
    649        }
    650
    651        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "get")
    652
    653        url = f"https://yd-saas-toc.wyzecam.com{url_path}"
    654
    655        response_json = await self._auth_lib.get(url, params=payload)
    656
    657        check_for_errors_lock(self, response_json)
    658
    659        return response_json
    660
    661    async def _get_device_info(self, device: Device) -> Dict[Any, Any]:
    662        await self._auth_lib.refresh_if_should()
    663
    664        payload = {
    665            "phone_system_type": PHONE_SYSTEM_TYPE,
    666            "app_version": APP_VERSION,
    667            "app_ver": APP_VER,
    668            "device_mac": device.mac,
    669            "sc": "9f275790cab94a72bd206c8876429f3c",
    670            "ts": int(time.time()),
    671            "device_model": device.product_model,
    672            "sv": "c86fa16fc99d4d6580f82ef3b942e586",
    673            "access_token": self._auth_lib.token.access_token,
    674            "phone_id": PHONE_ID,
    675            "app_name": APP_NAME
    676        }
    677
    678        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_device_Info",
    679                                                  json=payload)
    680
    681        check_for_errors_standard(self, response_json)
    682
    683        return response_json
    684
    685    async def _get_iot_prop(self, url: str, device: Device, keys: str) -> Dict[Any, Any]:
    686        await self._auth_lib.refresh_if_should()
    687
    688        payload = olive_create_get_payload(device.mac, keys)
    689        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    690        headers = {
    691            'Accept-Encoding': 'gzip',
    692            'User-Agent': 'myapp',
    693            'appid': OLIVE_APP_ID,
    694            'appinfo': APP_INFO,
    695            'phoneid': PHONE_ID,
    696            'access_token': self._auth_lib.token.access_token,
    697            'signature2': signature
    698        }
    699
    700        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    701
    702        check_for_errors_iot(self, response_json)
    703
    704        return response_json
    705
    706    async def _set_iot_prop(self, url: str, device: Device, prop_key: str, value: Any) -> None:
    707        await self._auth_lib.refresh_if_should()
    708
    709        payload = olive_create_post_payload(device.mac, device.product_model, prop_key, value)
    710        signature = olive_create_signature(json.dumps(payload, separators=(',', ':')),
    711                                           self._auth_lib.token.access_token)
    712        headers = {
    713            'Accept-Encoding': 'gzip',
    714            'Content-Type': 'application/json',
    715            'User-Agent': 'myapp',
    716            'appid': OLIVE_APP_ID,
    717            'appinfo': APP_INFO,
    718            'phoneid': PHONE_ID,
    719            'access_token': self._auth_lib.token.access_token,
    720            'signature2': signature
    721        }
    722
    723        payload_str = json.dumps(payload, separators=(',', ':'))
    724
    725        response_json = await self._auth_lib.post(url, headers=headers, data=payload_str)
    726
    727        check_for_errors_iot(self, response_json)
    728
    729    async def _local_bulb_command(self, bulb, plist):
    730        # await self._auth_lib.refresh_if_should()
    731
    732        characteristics = {
    733            "mac": bulb.mac.upper(),
    734            "index": "1",
    735            "ts": str(int(time.time_ns() // 1000000)),
    736            "plist": plist
    737        }
    738
    739        characteristics_str = json.dumps(characteristics, separators=(',', ':'))
    740        characteristics_enc = wyze_encrypt(bulb.enr, characteristics_str)
    741
    742        payload = {
    743            "request": "set_status",
    744            "isSendQueue": 0,
    745            "characteristics": characteristics_enc
    746        }
    747
    748        # JSON likes to add a second \ so we have to remove it for the bulb to be happy
    749        payload_str = json.dumps(payload, separators=(',', ':')).replace('\\\\', '\\')
    750
    751        url = "http://%s:88/device_request" % bulb.ip
    752
    753        try:
    754            async with aiohttp.ClientSession() as session:
    755                async with session.post(url, data=payload_str) as response:
    756                    print(await response.text())
    757        except aiohttp.ClientConnectionError:
    758            _LOGGER.warning("Failed to connect to bulb %s, reverting to cloud." % bulb.mac)
    759            await self._run_action_list(bulb, plist)
    760            bulb.cloud_fallback = True
    761
    762    async def _get_plug_history(
    763        self, device: Device, start_time, end_time
    764    ) -> Dict[Any, Any]:
    765        """Wraps the https://api.wyzecam.com/app/v2/plug/usage_record_list endpoint"""
    766
    767        await self._auth_lib.refresh_if_should()
    768
    769        payload = {
    770            "phone_id": PHONE_ID,
    771            "date_begin": start_time,
    772            "date_end": end_time,
    773            "app_name": APP_NAME,
    774            "app_version": APP_VERSION,
    775            "sc": SC,
    776            "device_mac": device.mac,
    777            "sv": SV,
    778            "phone_system_type": PHONE_SYSTEM_TYPE,
    779            "app_ver": APP_VER,
    780            "ts": int(time.time()),
    781            "access_token": self._auth_lib.token.access_token,
    782        }
    783
    784        response_json = await self._auth_lib.post(
    785            "https://api.wyzecam.com/app/v2/plug/usage_record_list", json=payload
    786        )
    787
    788        check_for_errors_standard(self, response_json)
    789
    790        return response_json["data"]["usage_record_list"]

class BaseService: View Source

     29class BaseService:
     30    """Base service class for interacting with Wyze devices."""
     31    _devices: Optional[List[Device]] = None
     32    _last_updated_time: time = 0  # preload a value of 0 so that comparison will succeed on the first run
     33    _min_update_time = 1200  # lets let the device_params update every 20 minutes for now. This could probably reduced signicficantly.
     34    _update_lock: asyncio.Lock = asyncio.Lock() # fmt: skip
     35    _update_manager: UpdateManager = UpdateManager()
     36    _update_loop = None
     37    _updater: DeviceUpdater = None
     38    _updater_dict = {}
     39
     40    def __init__(self, auth_lib: WyzeAuthLib):
     41        """Initialize the base service."""
     42        self._auth_lib = auth_lib
     43
     44    @staticmethod
     45    async def start_update_manager():
     46        """Start the update manager."""
     47        if BaseService._update_loop is None:
     48            BaseService._update_loop = asyncio.get_event_loop()
     49            BaseService._update_loop.create_task(BaseService._update_manager.update_next())
     50
     51    def register_updater(self, device: Device, interval):
     52        """
     53        Register a device to be updated at a specific interval.
     54
     55        Parameters
     56        ----------
     57        device : Device
     58            The device to register.
     59        interval : int
     60            The interval in seconds.
     61        """
     62        self._updater = DeviceUpdater(self, device, interval)
     63        BaseService._update_manager.add_updater(self._updater)
     64        self._updater_dict[self._updater.device] = self._updater
     65
     66    def unregister_updater(self, device: Device):
     67        if self._updater:
     68            BaseService._update_manager.del_updater(self._updater_dict[device])
     69            del self._updater_dict[device]
     70
     71    async def set_push_info(self, on: bool) -> None:
     72        await self._auth_lib.refresh_if_should()
     73
     74        url = "https://api.wyzecam.com/app/user/set_push_info"
     75        payload = {
     76            "phone_system_type": PHONE_SYSTEM_TYPE,
     77            "app_version": APP_VERSION,
     78            "app_ver": APP_VER,
     79            "push_switch": "1" if on else "2",
     80            "sc": SC,
     81            "ts": int(time.time()),
     82            "sv": SV,
     83            "access_token": self._auth_lib.token.access_token,
     84            "phone_id": PHONE_ID,
     85            "app_name": APP_NAME
     86        }
     87
     88        response_json = await self._auth_lib.post(url, json=payload)
     89
     90        check_for_errors_standard(self, response_json)
     91
     92    async def get_user_profile(self) -> Dict[Any, Any]:
     93        await self._auth_lib.refresh_if_should()
     94
     95        payload = olive_create_user_info_payload()
     96        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
     97        headers = {
     98            'Accept-Encoding': 'gzip',
     99            'User-Agent': 'myapp',
    100            'appid': OLIVE_APP_ID,
    101            'appinfo': APP_INFO,
    102            'phoneid': PHONE_ID,
    103            'access_token': self._auth_lib.token.access_token,
    104            'signature2': signature
    105        }
    106
    107        url = 'https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile'
    108
    109        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    110
    111        return response_json
    112
    113    async def get_object_list(self) -> List[Device]:
    114        """
    115        Wraps the api.wyzecam.com/app/v2/home_page/get_object_list endpoint
    116
    117        :return: List of devices
    118        """
    119        await self._auth_lib.refresh_if_should()
    120
    121        payload = {
    122            "phone_system_type": PHONE_SYSTEM_TYPE,
    123            "app_version": APP_VERSION,
    124            "app_ver": APP_VER,
    125            "sc": "9f275790cab94a72bd206c8876429f3c",
    126            "ts": int(time.time()),
    127            "sv": "9d74946e652647e9b6c9d59326aef104",
    128            "access_token": self._auth_lib.token.access_token,
    129            "phone_id": PHONE_ID,
    130            "app_name": APP_NAME
    131        }
    132
    133        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/home_page/get_object_list",
    134                                                  json=payload)
    135
    136        check_for_errors_standard(self, response_json)
    137        # Cache the devices so that update calls can pull more recent device_params
    138        BaseService._devices = [Device(device) for device in response_json['data']['device_list']]
    139
    140        return BaseService._devices
    141
    142    async def get_updated_params(self, device_mac: str = None) -> Dict[str, Optional[Any]]:
    143        if time.time() - BaseService._last_updated_time >= BaseService._min_update_time:
    144            await self.get_object_list()
    145            BaseService._last_updated_time = time.time()
    146        ret_params = {}
    147        for dev in BaseService._devices:
    148            if dev.mac == device_mac:
    149                ret_params = dev.device_params
    150        return ret_params
    151
    152    async def _get_property_list(self, device: Device) -> List[Tuple[PropertyIDs, Any]]:
    153        """
    154        Wraps the api.wyzecam.com/app/v2/device/get_property_list endpoint
    155
    156        :param device: Device to get properties for
    157        :return: List of PropertyIDs and values
    158        """
    159
    160        await self._auth_lib.refresh_if_should()
    161
    162        payload = {
    163            "phone_system_type": PHONE_SYSTEM_TYPE,
    164            "app_version": APP_VERSION,
    165            "app_ver": APP_VER,
    166            "sc": "9f275790cab94a72bd206c8876429f3c",
    167            "ts": int(time.time()),
    168            "sv": "9d74946e652647e9b6c9d59326aef104",
    169            "access_token": self._auth_lib.token.access_token,
    170            "phone_id": PHONE_ID,
    171            "app_name": APP_NAME,
    172            "device_model": device.product_model,
    173            "device_mac": device.mac,
    174            "target_pid_list": []
    175        }
    176
    177        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_property_list",
    178                                                  json=payload)
    179
    180        check_for_errors_standard(self, response_json)
    181        properties = response_json['data']['property_list']
    182        property_list = []
    183        for prop in properties:
    184            try:
    185                property_id = PropertyIDs(prop['pid'])
    186                property_list.append((
    187                    property_id,
    188                    prop['value']
    189                ))
    190            except ValueError:
    191                pass
    192
    193        return property_list
    194
    195    async def _set_property_list(self, device: Device, plist: List[Dict[str, str]]) -> None:
    196        """
    197        Wraps the api.wyzecam.com/app/v2/device/set_property_list endpoint
    198
    199        :param device: The device for which to set the property(ies)
    200        :param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
    201        :return:
    202        """
    203
    204        await self._auth_lib.refresh_if_should()
    205
    206        payload = {
    207            "phone_system_type": PHONE_SYSTEM_TYPE,
    208            "app_version": APP_VERSION,
    209            "app_ver": APP_VER,
    210            "sc": "9f275790cab94a72bd206c8876429f3c",
    211            "ts": int(time.time()),
    212            "sv": "9d74946e652647e9b6c9d59326aef104",
    213            "access_token": self._auth_lib.token.access_token,
    214            "phone_id": PHONE_ID,
    215            "app_name": APP_NAME,
    216            "property_list": plist,
    217            "device_model": device.product_model,
    218            "device_mac": device.mac
    219        }
    220
    221        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/set_property_list",
    222                                                  json=payload)
    223
    224        check_for_errors_standard(self, response_json)
    225
    226    async def _run_action_list(self, device: Device, plist: List[Dict[Any, Any]]) -> None:
    227        """
    228        Wraps the api.wyzecam.com/app/v2/auto/run_action_list endpoint
    229
    230        :param device: The device for which to run the action list
    231        :param plist: A list of properties [{"pid": pid, "pvalue": pvalue},...]
    232        """
    233        await self._auth_lib.refresh_if_should()
    234
    235        payload = {
    236            "phone_system_type": PHONE_SYSTEM_TYPE,
    237            "app_version": APP_VERSION,
    238            "app_ver": APP_VER,
    239            "sc": "9f275790cab94a72bd206c8876429f3c",
    240            "ts": int(time.time()),
    241            "sv": "9d74946e652647e9b6c9d59326aef104",
    242            "access_token": self._auth_lib.token.access_token,
    243            "phone_id": PHONE_ID,
    244            "app_name": APP_NAME,
    245            "action_list": [
    246                {
    247                    "instance_id": device.mac,
    248                    "action_params": {
    249                        "list": [
    250                            {
    251                                "mac": device.mac,
    252                                "plist": plist
    253                            }
    254                        ]
    255                    },
    256                    "provider_key": device.product_model,
    257                    "action_key": "set_mesh_property"
    258                }
    259            ]
    260        }
    261
    262        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/auto/run_action_list",
    263                                                  json=payload)
    264
    265        check_for_errors_standard(self, response_json)
    266
    267    async def _get_event_list(self, count: int) -> Dict[Any, Any]:
    268        """
    269        Wraps the api.wyzecam.com/app/v2/device/get_event_list endpoint
    270
    271        :param count: Number of events to gather
    272        :return: Response from the server
    273        """
    274
    275        await self._auth_lib.refresh_if_should()
    276
    277        payload = {
    278            "phone_id": PHONE_ID,
    279            "begin_time": int((time.time() - (60 * 60)) * 1000),
    280            "event_type": "",
    281            "app_name": APP_NAME,
    282            "count": count,
    283            "app_version": APP_VERSION,
    284            "order_by": 2,
    285            "event_value_list": [
    286                "1",
    287                "13",
    288                "10",
    289                "12"
    290            ],
    291            "sc": "9f275790cab94a72bd206c8876429f3c",
    292            "device_mac_list": [],
    293            "event_tag_list": [],
    294            "sv": "782ced6909a44d92a1f70d582bbe88be",
    295            "end_time": int(time.time() * 1000),
    296            "phone_system_type": PHONE_SYSTEM_TYPE,
    297            "app_ver": APP_VER,
    298            "ts": 1623612037763,
    299            "device_mac": "",
    300            "access_token": self._auth_lib.token.access_token
    301        }
    302
    303        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_event_list",
    304                                                  json=payload)
    305
    306        check_for_errors_standard(self, response_json)
    307        return response_json
    308
    309    async def _run_action(self, device: Device, action: str) -> None:
    310        """
    311        Wraps the api.wyzecam.com/app/v2/auto/run_action endpoint
    312
    313        :param device: The device for which to run the action
    314        :param action: The action to run
    315        :return:
    316        """
    317
    318        await self._auth_lib.refresh_if_should()
    319
    320        payload = {
    321            "phone_system_type": PHONE_SYSTEM_TYPE,
    322            "app_version": APP_VERSION,
    323            "app_ver": APP_VER,
    324            "sc": "9f275790cab94a72bd206c8876429f3c",
    325            "ts": int(time.time()),
    326            "sv": "9d74946e652647e9b6c9d59326aef104",
    327            "access_token": self._auth_lib.token.access_token,
    328            "phone_id": PHONE_ID,
    329            "app_name": APP_NAME,
    330            "provider_key": device.product_model,
    331            "instance_id": device.mac,
    332            "action_key": action,
    333            "action_params": {},
    334            "custom_string": "",
    335        }
    336
    337        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/auto/run_action",
    338                                                  json=payload)
    339
    340        check_for_errors_standard(self, response_json)
    341
    342    async def _run_action_devicemgmt(self, device: Device, type: str, value: str) -> None:
    343        """
    344        Wraps the devicemgmt-service-beta.wyze.com/device-management/api/action/run_action endpoint
    345
    346        :param device: The device for which to run the action
    347        :param state: on or off
    348        :return:
    349        """
    350
    351        await self._auth_lib.refresh_if_should()
    352
    353        capabilities = devicemgmt_create_capabilities_payload(type, value)
    354
    355        payload = {
    356            "capabilities": [
    357                capabilities
    358            ],
    359            "nonce": int(time.time() * 1000),
    360            "targetInfo": {
    361                "id": device.mac,
    362                "productModel": device.product_model,
    363                "type": "DEVICE"
    364            },
    365            "transactionId": "0a5b20591fedd4du1b93f90743ba0csd" # OG cam needs this (doesn't matter what the value is)
    366        }
    367
    368        headers = {
    369            "authorization": self._auth_lib.token.access_token,
    370        }
    371
    372        response_json = await self._auth_lib.post("https://devicemgmt-service-beta.wyze.com/device-management/api/action/run_action",
    373                                                  json=payload, headers=headers)
    374
    375        check_for_errors_iot(self, response_json)
    376
    377    async def _set_toggle(self, device: Device, toggleType: DeviceMgmtToggleType, state: str) -> None:
    378        """
    379        Wraps the ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management endpoint
    380
    381        :param device: The device for which to get the state
    382        :param toggleType: Enum for the toggle type
    383        :param state: String state to set for the toggle
    384        """
    385
    386        await self._auth_lib.refresh_if_should()
    387
    388        payload = {
    389            "data": [
    390                {
    391                    "device_firmware": "1234567890",
    392                    "device_id": device.mac,
    393                    "device_model": device.product_model,
    394                    "page_id": [
    395                        toggleType.pageId
    396                    ],
    397                    "toggle_update": [
    398                        {
    399                            "toggle_id": toggleType.toggleId,
    400                            "toggle_status": state
    401                        }
    402                    ]
    403                }
    404            ],
    405            "nonce": str(int(time.time() * 1000))
    406        }
    407
    408
    409        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    410        headers = {
    411            "access_token": self._auth_lib.token.access_token,
    412            "timestamp": str(int(time.time() * 1000)),
    413            "appid": OLIVE_APP_ID,
    414            "source": SOURCE,
    415            "signature2": signature,
    416            "appplatform": APP_PLATFORM,
    417            "appversion": APP_VERSION,
    418            "requestid": "35374158s4s313b9a2be7c057f2da5d1"
    419        }
    420
    421        response_json = await self._auth_lib.put("https://ai-subscription-service-beta.wyzecam.com/v4/subscription-service/toggle-management",
    422                                                  json=payload, headers=headers)
    423
    424        check_for_errors_devicemgmt(self, response_json)
    425
    426    async def _get_iot_prop_devicemgmt(self, device: Device) -> Dict[str, Any]:
    427        """
    428        Wraps the devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop endpoint
    429
    430        :param device: The device for which to get the state
    431        :return:
    432        """
    433
    434        await self._auth_lib.refresh_if_should()
    435
    436        payload = {
    437            "capabilities": devicemgmt_get_iot_props_list(device.product_model),
    438            "nonce": int(time.time() * 1000),
    439            "targetInfo": {
    440                "id": device.mac,
    441                "productModel": device.product_model,
    442                "type": "DEVICE"
    443            }
    444        }
    445
    446        headers = {
    447            "authorization": self._auth_lib.token.access_token,
    448        }
    449
    450        response_json = await self._auth_lib.post("https://devicemgmt-service-beta.wyze.com/device-management/api/device-property/get_iot_prop",
    451                                                  json=payload, headers=headers)
    452
    453        check_for_errors_iot(self, response_json)
    454
    455        return response_json
    456
    457    async def _set_property(self, device: Device, pid: str, pvalue: str) -> None:
    458        """
    459        Wraps the api.wyzecam.com/app/v2/device/set_property endpoint
    460
    461        :param device: The device for which to set the property
    462        :param pid: The property id
    463        :param pvalue: The property value
    464        """
    465        await self._auth_lib.refresh_if_should()
    466
    467        payload = {
    468            "phone_system_type": PHONE_SYSTEM_TYPE,
    469            "app_version": APP_VERSION,
    470            "app_ver": APP_VER,
    471            "sc": "9f275790cab94a72bd206c8876429f3c",
    472            "ts": int(time.time()),
    473            "sv": "9d74946e652647e9b6c9d59326aef104",
    474            "access_token": self._auth_lib.token.access_token,
    475            "phone_id": PHONE_ID,
    476            "app_name": APP_NAME,
    477            "pvalue": pvalue,
    478            "pid": pid,
    479            "device_model": device.product_model,
    480            "device_mac": device.mac
    481        }
    482
    483        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/set_property",
    484                                                  json=payload)
    485
    486        check_for_errors_standard(self, response_json)
    487
    488    async def _monitoring_profile_active(self, hms_id: str, home: int, away: int) -> None:
    489        """
    490        Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/active endpoint
    491
    492        :param hms_id: The hms id
    493        :param home: 1 for home 0 for not
    494        :param away: 1 for away 0 for not
    495        :return:
    496        """
    497        await self._auth_lib.refresh_if_should()
    498
    499        url = "https://hms.api.wyze.com/api/v1/monitoring/v1/profile/active"
    500        query = olive_create_hms_patch_payload(hms_id)
    501        signature = olive_create_signature(query, self._auth_lib.token.access_token)
    502        headers = {
    503            'Accept-Encoding': 'gzip',
    504            'User-Agent': 'myapp',
    505            'appid': OLIVE_APP_ID,
    506            'appinfo': APP_INFO,
    507            'phoneid': PHONE_ID,
    508            'access_token': self._auth_lib.token.access_token,
    509            'signature2': signature,
    510            'Authorization': self._auth_lib.token.access_token
    511        }
    512        payload = [
    513            {
    514                "state": "home",
    515                "active": home
    516            },
    517            {
    518                "state": "away",
    519                "active": away
    520            }
    521        ]
    522        response_json = await self._auth_lib.patch(url, headers=headers, params=query, json=payload)
    523        check_for_errors_hms(self, response_json)
    524
    525    async def _get_plan_binding_list_by_user(self) -> Dict[Any, Any]:
    526        """
    527        Wraps the wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user endpoint
    528
    529        :return: The response to gathering the plan for the current user
    530        """
    531
    532        if self._auth_lib.should_refresh:
    533            await self._auth_lib.refresh()
    534
    535        url = "https://wyze-membership-service.wyzecam.com/platform/v2/membership/get_plan_binding_list_by_user"
    536        payload = olive_create_hms_payload()
    537        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    538        headers = {
    539            'Accept-Encoding': 'gzip',
    540            'User-Agent': 'myapp',
    541            'appid': OLIVE_APP_ID,
    542            'appinfo': APP_INFO,
    543            'phoneid': PHONE_ID,
    544            'access_token': self._auth_lib.token.access_token,
    545            'signature2': signature
    546        }
    547
    548        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    549        check_for_errors_hms(self, response_json)
    550        return response_json
    551
    552    async def _disable_reme_alarm(self, hms_id: str) -> None:
    553        """
    554        Wraps the hms.api.wyze.com/api/v1/reme-alarm endpoint
    555
    556        :param hms_id: The hms_id for the account
    557        """
    558        await self._auth_lib.refresh_if_should()
    559
    560        url = "https://hms.api.wyze.com/api/v1/reme-alarm"
    561        payload = {
    562            "hms_id": hms_id,
    563            "remediation_id": "emergency"
    564        }
    565        headers = {
    566            "Authorization": self._auth_lib.token.access_token
    567        }
    568
    569        response_json = await self._auth_lib.delete(url, headers=headers, json=payload)
    570
    571        check_for_errors_hms(self, response_json)
    572
    573    async def _monitoring_profile_state_status(self, hms_id: str) -> Dict[Any, Any]:
    574        """
    575        Wraps the hms.api.wyze.com/api/v1/monitoring/v1/profile/state-status endpoint
    576
    577        :param hms_id: The hms_id
    578        :return: The response that includes the status
    579        """
    580        if self._auth_lib.should_refresh:
    581            await self._auth_lib.refresh()
    582
    583        url = "https://hms.api.wyze.com/api/v1/monitoring/v1/profile/state-status"
    584        query = olive_create_hms_get_payload(hms_id)
    585        signature = olive_create_signature(query, self._auth_lib.token.access_token)
    586        headers = {
    587            'User-Agent': 'myapp',
    588            'appid': OLIVE_APP_ID,
    589            'appinfo': APP_INFO,
    590            'phoneid': PHONE_ID,
    591            'access_token': self._auth_lib.token.access_token,
    592            'signature2': signature,
    593            'Authorization': self._auth_lib.token.access_token,
    594            'Content-Type': "application/json"
    595        }
    596
    597        response_json = await self._auth_lib.get(url, headers=headers, params=query)
    598
    599        check_for_errors_hms(self, response_json)
    600        return response_json
    601
    602    async def _lock_control(self, device: Device, action: str) -> None:
    603        await self._auth_lib.refresh_if_should()
    604
    605        url_path = "/openapi/lock/v1/control"
    606
    607        device_uuid = device.mac.split(".")[-1]
    608
    609        payload = {
    610            "uuid": device_uuid,
    611            "action": action  # "remoteLock" or "remoteUnlock"
    612        }
    613        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "post")
    614
    615        url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/control"
    616
    617        response_json = await self._auth_lib.post(url, json=payload)
    618
    619        check_for_errors_lock(self, response_json)
    620
    621    async def _get_lock_info(self, device: Device) -> Dict[str, Optional[Any]]:
    622        await self._auth_lib.refresh_if_should()
    623
    624        url_path = "/openapi/lock/v1/info"
    625
    626        device_uuid = device.mac.split(".")[-1]
    627
    628        payload = {
    629            "uuid": device_uuid,
    630            "with_keypad": "1"
    631        }
    632
    633        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "get")
    634
    635        url = "https://yd-saas-toc.wyzecam.com/openapi/lock/v1/info"
    636
    637        response_json = await self._auth_lib.get(url, params=payload)
    638
    639        check_for_errors_lock(self, response_json)
    640
    641        return response_json
    642
    643    async def _get_lock_ble_token(self, device: Device) -> Dict[str, Optional[Any]]:
    644        await self._auth_lib.refresh_if_should()
    645
    646        url_path = "/openapi/lock/v1/ble/token"
    647
    648        payload = {
    649            "uuid": device.mac
    650        }
    651
    652        payload = ford_create_payload(self._auth_lib.token.access_token, payload, url_path, "get")
    653
    654        url = f"https://yd-saas-toc.wyzecam.com{url_path}"
    655
    656        response_json = await self._auth_lib.get(url, params=payload)
    657
    658        check_for_errors_lock(self, response_json)
    659
    660        return response_json
    661
    662    async def _get_device_info(self, device: Device) -> Dict[Any, Any]:
    663        await self._auth_lib.refresh_if_should()
    664
    665        payload = {
    666            "phone_system_type": PHONE_SYSTEM_TYPE,
    667            "app_version": APP_VERSION,
    668            "app_ver": APP_VER,
    669            "device_mac": device.mac,
    670            "sc": "9f275790cab94a72bd206c8876429f3c",
    671            "ts": int(time.time()),
    672            "device_model": device.product_model,
    673            "sv": "c86fa16fc99d4d6580f82ef3b942e586",
    674            "access_token": self._auth_lib.token.access_token,
    675            "phone_id": PHONE_ID,
    676            "app_name": APP_NAME
    677        }
    678
    679        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/device/get_device_Info",
    680                                                  json=payload)
    681
    682        check_for_errors_standard(self, response_json)
    683
    684        return response_json
    685
    686    async def _get_iot_prop(self, url: str, device: Device, keys: str) -> Dict[Any, Any]:
    687        await self._auth_lib.refresh_if_should()
    688
    689        payload = olive_create_get_payload(device.mac, keys)
    690        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
    691        headers = {
    692            'Accept-Encoding': 'gzip',
    693            'User-Agent': 'myapp',
    694            'appid': OLIVE_APP_ID,
    695            'appinfo': APP_INFO,
    696            'phoneid': PHONE_ID,
    697            'access_token': self._auth_lib.token.access_token,
    698            'signature2': signature
    699        }
    700
    701        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    702
    703        check_for_errors_iot(self, response_json)
    704
    705        return response_json
    706
    707    async def _set_iot_prop(self, url: str, device: Device, prop_key: str, value: Any) -> None:
    708        await self._auth_lib.refresh_if_should()
    709
    710        payload = olive_create_post_payload(device.mac, device.product_model, prop_key, value)
    711        signature = olive_create_signature(json.dumps(payload, separators=(',', ':')),
    712                                           self._auth_lib.token.access_token)
    713        headers = {
    714            'Accept-Encoding': 'gzip',
    715            'Content-Type': 'application/json',
    716            'User-Agent': 'myapp',
    717            'appid': OLIVE_APP_ID,
    718            'appinfo': APP_INFO,
    719            'phoneid': PHONE_ID,
    720            'access_token': self._auth_lib.token.access_token,
    721            'signature2': signature
    722        }
    723
    724        payload_str = json.dumps(payload, separators=(',', ':'))
    725
    726        response_json = await self._auth_lib.post(url, headers=headers, data=payload_str)
    727
    728        check_for_errors_iot(self, response_json)
    729
    730    async def _local_bulb_command(self, bulb, plist):
    731        # await self._auth_lib.refresh_if_should()
    732
    733        characteristics = {
    734            "mac": bulb.mac.upper(),
    735            "index": "1",
    736            "ts": str(int(time.time_ns() // 1000000)),
    737            "plist": plist
    738        }
    739
    740        characteristics_str = json.dumps(characteristics, separators=(',', ':'))
    741        characteristics_enc = wyze_encrypt(bulb.enr, characteristics_str)
    742
    743        payload = {
    744            "request": "set_status",
    745            "isSendQueue": 0,
    746            "characteristics": characteristics_enc
    747        }
    748
    749        # JSON likes to add a second \ so we have to remove it for the bulb to be happy
    750        payload_str = json.dumps(payload, separators=(',', ':')).replace('\\\\', '\\')
    751
    752        url = "http://%s:88/device_request" % bulb.ip
    753
    754        try:
    755            async with aiohttp.ClientSession() as session:
    756                async with session.post(url, data=payload_str) as response:
    757                    print(await response.text())
    758        except aiohttp.ClientConnectionError:
    759            _LOGGER.warning("Failed to connect to bulb %s, reverting to cloud." % bulb.mac)
    760            await self._run_action_list(bulb, plist)
    761            bulb.cloud_fallback = True
    762
    763    async def _get_plug_history(
    764        self, device: Device, start_time, end_time
    765    ) -> Dict[Any, Any]:
    766        """Wraps the https://api.wyzecam.com/app/v2/plug/usage_record_list endpoint"""
    767
    768        await self._auth_lib.refresh_if_should()
    769
    770        payload = {
    771            "phone_id": PHONE_ID,
    772            "date_begin": start_time,
    773            "date_end": end_time,
    774            "app_name": APP_NAME,
    775            "app_version": APP_VERSION,
    776            "sc": SC,
    777            "device_mac": device.mac,
    778            "sv": SV,
    779            "phone_system_type": PHONE_SYSTEM_TYPE,
    780            "app_ver": APP_VER,
    781            "ts": int(time.time()),
    782            "access_token": self._auth_lib.token.access_token,
    783        }
    784
    785        response_json = await self._auth_lib.post(
    786            "https://api.wyzecam.com/app/v2/plug/usage_record_list", json=payload
    787        )
    788
    789        check_for_errors_standard(self, response_json)
    790
    791        return response_json["data"]["usage_record_list"]

Base service class for interacting with Wyze devices.

BaseService(auth_lib: wyzeapy.wyze_auth_lib.WyzeAuthLib) View Source

    40    def __init__(self, auth_lib: WyzeAuthLib):
    41        """Initialize the base service."""
    42        self._auth_lib = auth_lib

Initialize the base service.

@staticmethod
async def start_update_manager(): View Source

    44    @staticmethod
    45    async def start_update_manager():
    46        """Start the update manager."""
    47        if BaseService._update_loop is None:
    48            BaseService._update_loop = asyncio.get_event_loop()
    49            BaseService._update_loop.create_task(BaseService._update_manager.update_next())

Start the update manager.

def register_updater(self, device: wyzeapy.types.Device, interval): View Source

    51    def register_updater(self, device: Device, interval):
    52        """
    53        Register a device to be updated at a specific interval.
    54
    55        Parameters
    56        ----------
    57        device : Device
    58            The device to register.
    59        interval : int
    60            The interval in seconds.
    61        """
    62        self._updater = DeviceUpdater(self, device, interval)
    63        BaseService._update_manager.add_updater(self._updater)
    64        self._updater_dict[self._updater.device] = self._updater

Register a device to be updated at a specific interval.

## Parameters

device : Device The device to register. interval : int The interval in seconds.

def unregister_updater(self, device: wyzeapy.types.Device): View Source

    66    def unregister_updater(self, device: Device):
    67        if self._updater:
    68            BaseService._update_manager.del_updater(self._updater_dict[device])
    69            del self._updater_dict[device]

async def set_push_info(self, on: bool) -> None: View Source

    71    async def set_push_info(self, on: bool) -> None:
    72        await self._auth_lib.refresh_if_should()
    73
    74        url = "https://api.wyzecam.com/app/user/set_push_info"
    75        payload = {
    76            "phone_system_type": PHONE_SYSTEM_TYPE,
    77            "app_version": APP_VERSION,
    78            "app_ver": APP_VER,
    79            "push_switch": "1" if on else "2",
    80            "sc": SC,
    81            "ts": int(time.time()),
    82            "sv": SV,
    83            "access_token": self._auth_lib.token.access_token,
    84            "phone_id": PHONE_ID,
    85            "app_name": APP_NAME
    86        }
    87
    88        response_json = await self._auth_lib.post(url, json=payload)
    89
    90        check_for_errors_standard(self, response_json)

async def get_user_profile(self) -> Dict[Any, Any]: View Source

     92    async def get_user_profile(self) -> Dict[Any, Any]:
     93        await self._auth_lib.refresh_if_should()
     94
     95        payload = olive_create_user_info_payload()
     96        signature = olive_create_signature(payload, self._auth_lib.token.access_token)
     97        headers = {
     98            'Accept-Encoding': 'gzip',
     99            'User-Agent': 'myapp',
    100            'appid': OLIVE_APP_ID,
    101            'appinfo': APP_INFO,
    102            'phoneid': PHONE_ID,
    103            'access_token': self._auth_lib.token.access_token,
    104            'signature2': signature
    105        }
    106
    107        url = 'https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile'
    108
    109        response_json = await self._auth_lib.get(url, headers=headers, params=payload)
    110
    111        return response_json

async def get_object_list(self) -> List[wyzeapy.types.Device]: View Source

    113    async def get_object_list(self) -> List[Device]:
    114        """
    115        Wraps the api.wyzecam.com/app/v2/home_page/get_object_list endpoint
    116
    117        :return: List of devices
    118        """
    119        await self._auth_lib.refresh_if_should()
    120
    121        payload = {
    122            "phone_system_type": PHONE_SYSTEM_TYPE,
    123            "app_version": APP_VERSION,
    124            "app_ver": APP_VER,
    125            "sc": "9f275790cab94a72bd206c8876429f3c",
    126            "ts": int(time.time()),
    127            "sv": "9d74946e652647e9b6c9d59326aef104",
    128            "access_token": self._auth_lib.token.access_token,
    129            "phone_id": PHONE_ID,
    130            "app_name": APP_NAME
    131        }
    132
    133        response_json = await self._auth_lib.post("https://api.wyzecam.com/app/v2/home_page/get_object_list",
    134                                                  json=payload)
    135
    136        check_for_errors_standard(self, response_json)
    137        # Cache the devices so that update calls can pull more recent device_params
    138        BaseService._devices = [Device(device) for device in response_json['data']['device_list']]
    139
    140        return BaseService._devices

Wraps the api.wyzecam.com/app/v2/home_page/get_object_list endpoint

###### Returns

> List of devices

async def get_updated_params(self, device_mac: str = None) -> Dict[str, Optional[Any]]: View Source

    142    async def get_updated_params(self, device_mac: str = None) -> Dict[str, Optional[Any]]:
    143        if time.time() - BaseService._last_updated_time >= BaseService._min_update_time:
    144            await self.get_object_list()
    145            BaseService._last_updated_time = time.time()
    146        ret_params = {}
    147        for dev in BaseService._devices:
    148            if dev.mac == device_mac:
    149                ret_params = dev.device_params
    150        return ret_params