From 2e6c48f610b996342475f96a7e591d0c5c20d91b Mon Sep 17 00:00:00 2001 From: Joshua Mulliken Date: Mon, 17 May 2021 22:29:13 -0400 Subject: [PATCH] Fix for lock error --- setup.cfg | 2 +- src/wyzeapy/base_client.py | 22 ++++---- src/wyzeapy/client.py | 6 ++- tests/__init__.py | 5 ++ tests/test_client.py | 104 +++++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_client.py diff --git a/setup.cfg b/setup.cfg index 5d1b76a..e48ba39 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] # replace with your username: name = wyzeapy -version = 0.0.23-beta.8 +version = 0.0.23-beta.9 author = Mulliken LLC author_email = joshua@mulliken.net description = Python client for private Wyze API diff --git a/src/wyzeapy/base_client.py b/src/wyzeapy/base_client.py index 3553043..eb8360a 100644 --- a/src/wyzeapy/base_client.py +++ b/src/wyzeapy/base_client.py @@ -6,6 +6,7 @@ import datetime import hashlib import json +import logging import time from typing import Any @@ -19,6 +20,7 @@ from .exceptions import * from .types import ResponseCodes, Device, DeviceTypes, ThermostatProps, Group +_LOGGER = logging.getLogger(__name__) class BaseClient: access_token = "" @@ -27,6 +29,9 @@ class BaseClient: def __init__(self): self._session: CacheControl = cachecontrol.CacheControl(requests.Session()) + def __del__(self): + self._session.close() + def login(self, email, password) -> bool: email = email password = password @@ -40,17 +45,18 @@ def login(self, email, password) -> bool: "X-API-Key": API_KEY } - response_json = self._session.post("https://auth-prod.api.wyze.com/user/login", + response_json: dict = self._session.post("https://auth-prod.api.wyze.com/user/login", headers=headers, json=login_payload).json() - try: - self.access_token = response_json['access_token'] - self.refresh_token = response_json['refresh_token'] - return True - except KeyError as e: - print(e) + if response_json.get('errorCode') is not None: + _LOGGER.error(f"Unable to login with response from Wyze: {response_json}") return False + self.access_token = response_json['access_token'] + self.refresh_token = response_json['refresh_token'] + + return True + def can_login(self, username, password): return self.login(username, password) @@ -353,8 +359,6 @@ def lock_control(self, device: Device, action: str): response_json = self._session.post(url, json=payload).json() - self.check_for_errors_lock(response_json) - return response_json def thermostat_get_iot_prop(self, device: Device): diff --git a/src/wyzeapy/client.py b/src/wyzeapy/client.py index b9a9da6..699a2a1 100644 --- a/src/wyzeapy/client.py +++ b/src/wyzeapy/client.py @@ -19,7 +19,11 @@ def __init__(self, email, password): self.password = password self.client = BaseClient() - self.client.login(self.email, self.password) + self._valid_login = self.client.login(self.email, self.password) + + @property + def valid_login(self): + return self._valid_login def reauthenticate(self) -> None: self.client.login(self.email, self.password) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..1721dc1 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2021. Mulliken, LLC - All Rights Reserved +# You may use, distribute and modify this code under the terms +# of the attached license. You should have received a copy of +# the license with this file. If not, please write to: +# joshua@mulliken.net to receive a copy \ No newline at end of file diff --git a/tests/test_client.py b/tests/test_client.py new file mode 100644 index 0000000..81ed98c --- /dev/null +++ b/tests/test_client.py @@ -0,0 +1,104 @@ +# Copyright (c) 2021. Mulliken, LLC - All Rights Reserved +# You may use, distribute and modify this code under the terms +# of the attached license. You should have received a copy of +# the license with this file. If not, please write to: +# joshua@mulliken.net to receive a copy + +import os +import unittest +from unittest import mock +from typing import List + +from src.wyzeapy.client import Client +from src.wyzeapy.types import Device, ThermostatProps + + +class TestLogin(unittest.TestCase): + def test_can_login(self): + client = Client(os.getenv("WYZE_EMAIL"), os.getenv("WYZE_PASSWORD")) + + self.assertTrue(client.valid_login) + + def test_bad_login(self): + client = Client("BadEmail@example.com", "BadPassword123") + + self.assertFalse(client.valid_login) + + +class TestFunctions(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls._client = Client(os.getenv("WYZE_EMAIL"), os.getenv("WYZE_PASSWORD")) + + def test_get_devices_return_type(self): + devices = self._client.get_devices() + self.assertIsInstance(devices, List) + for device in devices: + self.assertIsInstance(device, Device) + + def test_turn_on_color_bulb(self): + test_bulb = Device({ + 'mac': '7C78B214CF40', + 'product_type': 'MeshLight', + 'product_model': 'WLPA19C' + }) + + self._client.turn_on(test_bulb) + + def test_turn_off_color_bulb(self): + test_bulb = Device({ + 'mac': '7C78B214CF40', + 'product_type': 'MeshLight', + 'product_model': 'WLPA19C' + }) + + self._client.turn_off(test_bulb) + + def test_turn_on_bulb(self): + test_bulb = Device({ + 'mac': '2CAA8E325C52', + 'product_type': 'Light', + 'product_model': 'WLPA19' + }) + + self._client.turn_on(test_bulb) + + def test_turn_off_bulb(self): + test_bulb = Device({ + 'mac': '2CAA8E325C52', + 'product_type': 'Light', + 'product_model': 'WLPA19' + }) + + self._client.turn_off(test_bulb) + + def test_unlock_door(self): + test_lock = Device({ + 'mac': 'YD.LO1.46c36fcce6c550e527ff79bd8cef59c2', + 'product_type': 'Lock', + 'product_model': 'WLCK1' + }) + + self._client.turn_off(test_lock) + + def test_lock_door(self): + test_lock = Device({ + 'mac': 'YD.LO1.46c36fcce6c550e527ff79bd8cef59c2', + 'product_type': 'Lock', + 'product_model': 'WLCK1' + }) + + self._client.turn_on(test_lock) + + def test_can_read_thermostat(self): + test_thermostat = Device({ + 'mac': 'CO_EA1_31304635143637394a414e75', + 'product_type': 'Thermostat', + 'product_model': 'CO_EA1' + }) + + info = self._client.get_thermostat_info(test_thermostat) + + self.assertIsInstance(info, List) + for item in info: + self.assertIsInstance(info, ThermostatProps)