wyzeapy

  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 logging
  7from inspect import iscoroutinefunction
  8from typing import List, Optional, Set, Callable
  9
 10from .const import PHONE_SYSTEM_TYPE, APP_VERSION, SC, APP_VER, SV, PHONE_ID, APP_NAME, OLIVE_APP_ID, APP_INFO
 11from .crypto import olive_create_signature
 12from .exceptions import TwoFactorAuthenticationEnabled
 13from .payload_factory import olive_create_user_info_payload
 14from .services.base_service import BaseService
 15from .services.bulb_service import BulbService
 16from .services.camera_service import CameraService
 17from .services.hms_service import HMSService
 18from .services.lock_service import LockService
 19from .services.sensor_service import SensorService
 20from .services.switch_service import SwitchService, SwitchUsageService
 21from .services.thermostat_service import ThermostatService
 22from .services.wall_switch_service import WallSwitchService
 23from .wyze_auth_lib import WyzeAuthLib, Token
 24
 25_LOGGER = logging.getLogger(__name__)
 26
 27
 28class Wyzeapy:
 29    """A Python module to assist developers in interacting with the Wyze service API.
 30    
 31    This class provides methods for authentication, device management, and accessing
 32    various Wyze device services including:
 33    
 34    * **Bulbs** - Control brightness, color, and power state
 35    * **Switches** - Toggle power and monitor usage
 36    * **Cameras** - Access video streams and control settings
 37    * **Thermostats** - Manage temperature settings and modes
 38    * **Locks** - Control and monitor door locks
 39    * **Sensors** - Monitor motion, contact, and environmental sensors
 40    * **HMS** - Manage home monitoring system
 41    
 42    Most interactions with Wyze devices should go through this class.
 43    """
 44    # _client: Client
 45    _auth_lib: WyzeAuthLib
 46
 47    def __init__(self):
 48        self._bulb_service = None
 49        self._switch_service = None
 50        self._camera_service = None
 51        self._thermostat_service = None
 52        self._hms_service = None
 53        self._lock_service = None
 54        self._sensor_service = None
 55        self._wall_switch_service = None
 56        self._switch_usage_service = None
 57        self._email = None
 58        self._password = None
 59        self._key_id = None
 60        self._api_key = None
 61        self._service: Optional[BaseService] = None
 62        self._token_callbacks: List[Callable] = []
 63
 64    @classmethod
 65    async def create(cls):
 66        """
 67        Creates and initializes the Wyzeapy class asynchronously.
 68        
 69        This factory method provides a way to instantiate the class using async/await syntax,
 70        though it's currently a simple implementation that may be expanded in the future.
 71        
 72        **Returns:**
 73            `Wyzeapy`: A new instance of the Wyzeapy class ready for authentication.
 74        """
 75        self = cls()
 76        return self
 77
 78    async def login(
 79        self, email, password, key_id, api_key, token: Optional[Token] = None
 80    ):
 81        """
 82        Authenticates with the Wyze API and retrieves the user's access token.
 83        
 84        This method handles the authentication process, including token management
 85        and service initialization. If two-factor authentication is enabled on the account,
 86        it will raise an exception requiring the use of `login_with_2fa()` instead.
 87        
 88        **Args:**
 89        * `email` (str): User's email address for Wyze account
 90        * `password` (str): User's password for Wyze account
 91        * `key_id` (str): Key ID for third-party API access
 92        * `api_key` (str): API Key for third-party API access
 93        * `token` (Optional[Token], optional): Existing token from a previous session. Defaults to None.
 94        
 95        **Raises:**
 96        * `TwoFactorAuthenticationEnabled`: When the account has 2FA enabled and requires verification
 97        """
 98
 99        self._email = email
100        self._password = password
101        self._key_id = key_id
102        self._api_key = api_key
103
104        try:
105            self._auth_lib = await WyzeAuthLib.create(
106                email, password, key_id, api_key, token, self.execute_token_callbacks
107            )
108            if token:
109                # User token supplied, refresh on startup
110                await self._auth_lib.refresh()
111            else:
112                await self._auth_lib.get_token_with_username_password(
113                    email, password, key_id, api_key
114                )
115            self._service = BaseService(self._auth_lib)
116        except TwoFactorAuthenticationEnabled as error:
117            raise error
118
119    async def login_with_2fa(self, verification_code) -> Token:
120        """
121        Completes the login process for accounts with two-factor authentication enabled.
122        
123        This method should be called after receiving a `TwoFactorAuthenticationEnabled`
124        exception from the `login()` method. It completes the authentication process
125        using the verification code sent to the user.
126        
127        **Args:**
128        * `verification_code` (str): The 2FA verification code received by the user
129            
130        **Returns:**
131        * `Token`: The authenticated user token object
132        """
133
134        _LOGGER.debug(f"Verification Code: {verification_code}")
135
136        await self._auth_lib.get_token_with_2fa(verification_code)
137        self._service = BaseService(self._auth_lib)
138        return self._auth_lib.token
139
140    async def execute_token_callbacks(self, token: Token):
141        """
142        Sends the token to all registered callback functions.
143        
144        This method is called internally whenever the token is refreshed or updated,
145        allowing external components to stay in sync with token changes.
146        
147        **Args:**
148        * `token` (Token): The current user token object
149        """
150        for callback in self._token_callbacks:
151            if iscoroutinefunction(callback):
152                await callback(token)
153            else:
154                callback(token)
155
156    def register_for_token_callback(self, callback_function):
157        """
158        Registers a callback function to be called whenever the user's token is modified.
159        
160        This allows external components to be notified of token changes for persistence
161        or other token-dependent operations.
162        
163        **Args:**
164        * `callback_function`: A function that accepts a Token object as its parameter
165        
166        **Example:**
167        ```python
168        def token_updated(token):
169            print(f"Token refreshed: {token.access_token[:10]}...")
170            
171        wyze = Wyzeapy()
172        wyze.register_for_token_callback(token_updated)
173        ```
174        """
175        self._token_callbacks.append(callback_function)
176
177    def unregister_for_token_callback(self, callback_function):
178        """
179        Removes a previously registered token callback function.
180        
181        This stops the specified callback from receiving token updates.
182        
183        **Args:**
184        * `callback_function`: The callback function to remove from the notification list
185        """
186        self._token_callbacks.remove(callback_function)
187
188    @property
189    async def unique_device_ids(self) -> Set[str]:
190        """
191        Retrieves a set of all unique device IDs known to the Wyze server.
192        
193        This property fetches all devices associated with the account and
194        extracts their MAC addresses as unique identifiers.
195        
196        **Returns:**
197        * `Set[str]`: A set containing all unique device IDs (MAC addresses)
198        
199        **Example:**
200        ```python
201        device_ids = await wyze.unique_device_ids
202        print(f"Found {len(device_ids)} devices")
203        ```
204        """
205
206        devices = await self._service.get_object_list()
207        device_ids = set()
208        for device in devices:
209            device_ids.add(device.mac)
210
211        return device_ids
212
213    @property
214    async def notifications_are_on(self) -> bool:
215        """
216        Checks if push notifications are enabled for the account.
217        
218        This property queries the user profile to determine the current
219        notification settings status.
220        
221        **Returns:**
222        * `bool`: True if notifications are enabled, False otherwise
223        """
224
225        response_json = await self._service.get_user_profile()
226        return response_json['data']['notification']
227
228    async def enable_notifications(self):
229        """Enables push notifications for the Wyze account.
230        
231        This method updates the user's profile to turn on push notifications
232        for all supported devices and events.
233        
234        **Example:**
235        ```python
236        # Turn on notifications
237        await wyze.enable_notifications()
238        ```
239        """
240
241        await self._service.set_push_info(True)
242
243    async def disable_notifications(self):
244        """Disables push notifications for the Wyze account.
245        
246        This method updates the user's profile to turn off push notifications
247        for all devices and events.
248        
249        **Example:**
250        ```python
251        # Turn off notifications
252        await wyze.disable_notifications()
253        ```
254        """
255
256        await self._service.set_push_info(False)
257
258    @classmethod
259    async def valid_login(
260        cls, email: str, password: str, key_id: str, api_key: str
261    ) -> bool:
262        """
263        Validates if the provided credentials can successfully authenticate with the Wyze API.
264        
265        This method attempts to log in with the provided credentials and returns whether
266        the authentication was successful. It's useful for validating credentials without
267        needing to handle the full login process.
268        
269        **Args:**
270        * `email` (str): The user's email address
271        * `password` (str): The user's password
272        * `key_id` (str): Key ID for third-party API access
273        * `api_key` (str): API Key for third-party API access
274            
275        **Returns:**
276        * `bool`: True if the credentials are valid and authentication succeeded
277        
278        **Example:**
279        ```python
280        is_valid = await Wyzeapy.valid_login("user@example.com", "password123", "key_id", "api_key")
281        if is_valid:
282            print("Credentials are valid")
283        else:
284            print("Invalid credentials")
285        ```
286        """
287
288        self = cls()
289        await self.login(email, password, key_id, api_key)
290
291        return not self._auth_lib.should_refresh
292
293    @property
294    async def bulb_service(self) -> BulbService:
295        """Provides access to the Wyze Bulb service.
296        
297        This property lazily initializes and returns a BulbService instance
298        for controlling and monitoring Wyze bulbs.
299        
300        **Returns:**
301        * `BulbService`: An instance of the bulb service for interacting with Wyze bulbs
302        
303        **Example:**
304        ```python
305        # Get all bulbs
306        bulb_service = await wyze.bulb_service
307        bulbs = await bulb_service.get_bulbs()
308        ```
309        """
310
311        if self._bulb_service is None:
312            self._bulb_service = BulbService(self._auth_lib)
313        return self._bulb_service
314
315    @property
316    async def switch_service(self) -> SwitchService:
317        """Provides access to the Wyze Switch service.
318        
319        This property lazily initializes and returns a SwitchService instance
320        for controlling and monitoring Wyze plugs and switches.
321        
322        **Returns:**
323        * `SwitchService`: An instance of the switch service for interacting with Wyze switches
324        
325        **Example:**
326        ```python
327        # Get all switches
328        switch_service = await wyze.switch_service
329        switches = await switch_service.get_switches()
330        ```
331        """
332
333        if self._switch_service is None:
334            self._switch_service = SwitchService(self._auth_lib)
335        return self._switch_service
336
337    @property
338    async def camera_service(self) -> CameraService:
339        """Provides access to the Wyze Camera service.
340        
341        This property lazily initializes and returns a CameraService instance
342        for controlling and monitoring Wyze cameras.
343        
344        **Returns:**
345        * `CameraService`: An instance of the camera service for interacting with Wyze cameras
346        
347        **Example:**
348        ```python
349        # Get all cameras
350        camera_service = await wyze.camera_service
351        cameras = await camera_service.get_cameras()
352        ```
353        """
354
355        if self._camera_service is None:
356            self._camera_service = CameraService(self._auth_lib)
357        return self._camera_service
358
359    @property
360    async def thermostat_service(self) -> ThermostatService:
361        """Provides access to the Wyze Thermostat service.
362        
363        This property lazily initializes and returns a ThermostatService instance
364        for controlling and monitoring Wyze thermostats.
365        
366        **Returns:**
367        * `ThermostatService`: An instance of the thermostat service for interacting with Wyze thermostats
368        
369        **Example:**
370        ```python
371        # Get all thermostats
372        thermostat_service = await wyze.thermostat_service
373        thermostats = await thermostat_service.get_thermostats()
374        ```
375        """
376
377        if self._thermostat_service is None:
378            self._thermostat_service = ThermostatService(self._auth_lib)
379        return self._thermostat_service
380
381    @property
382    async def hms_service(self) -> HMSService:
383        """Provides access to the Wyze Home Monitoring Service (HMS).
384        
385        This property lazily initializes and returns an HMSService instance
386        for controlling and monitoring the Wyze home security system.
387        
388        **Returns:**
389        * `HMSService`: An instance of the HMS service for interacting with Wyze home monitoring
390        
391        **Example:**
392        ```python
393        # Get HMS status
394        hms_service = await wyze.hms_service
395        status = await hms_service.get_hms_status()
396        ```
397        """
398
399        if self._hms_service is None:
400            self._hms_service = await HMSService.create(self._auth_lib)
401        return self._hms_service
402
403    @property
404    async def lock_service(self) -> LockService:
405        """Provides access to the Wyze Lock service.
406        
407        This property lazily initializes and returns a LockService instance
408        for controlling and monitoring Wyze locks.
409        
410        **Returns:**
411        * `LockService`: An instance of the lock service for interacting with Wyze locks
412        
413        **Example:**
414        ```python
415        # Get all locks
416        lock_service = await wyze.lock_service
417        locks = await lock_service.get_locks()
418        ```
419        """
420
421        if self._lock_service is None:
422            self._lock_service = LockService(self._auth_lib)
423        return self._lock_service
424
425    @property
426    async def sensor_service(self) -> SensorService:
427        """Provides access to the Wyze Sensor service.
428        
429        This property lazily initializes and returns a SensorService instance
430        for monitoring Wyze sensors such as contact sensors, motion sensors, etc.
431        
432        **Returns:**
433        * `SensorService`: An instance of the sensor service for interacting with Wyze sensors
434        
435        **Example:**
436        ```python
437        # Get all sensors
438        sensor_service = await wyze.sensor_service
439        sensors = await sensor_service.get_sensors()
440        ```
441        """
442
443        if self._sensor_service is None:
444            self._sensor_service = SensorService(self._auth_lib)
445        return self._sensor_service
446
447    @property
448    async def wall_switch_service(self) -> WallSwitchService:
449        """Provides access to the Wyze Wall Switch service.
450        
451        This property lazily initializes and returns a WallSwitchService instance
452        for controlling and monitoring Wyze wall switches.
453        
454        **Returns:**
455        * `WallSwitchService`: An instance of the wall switch service for interacting with Wyze wall switches
456        
457        **Example:**
458        ```python
459        # Get all wall switches
460        wall_switch_service = await wyze.wall_switch_service
461        switches = await wall_switch_service.get_wall_switches()
462        ```
463        """
464
465        if self._wall_switch_service is None:
466            self._wall_switch_service = WallSwitchService(self._auth_lib)
467        return self._wall_switch_service
468
469    @property
470    async def switch_usage_service(self) -> SwitchUsageService:
471        """Provides access to the Wyze Switch Usage service.
472        
473        This property lazily initializes and returns a SwitchUsageService instance
474        for retrieving usage statistics from Wyze switches and plugs.
475        
476        **Returns:**
477        * `SwitchUsageService`: An instance of the switch usage service for accessing Wyze switch usage data
478        
479        **Example:**
480        ```python
481        # Get usage data for a switch
482        usage_service = await wyze.switch_usage_service
483        usage = await usage_service.get_usage_records(switch_mac)
484        ```
485        """
486        if self._switch_usage_service is None:
487            self._switch_usage_service = SwitchUsageService(self._auth_lib)
488        return self._switch_usage_service
class Wyzeapy:
 29class Wyzeapy:
 30    """A Python module to assist developers in interacting with the Wyze service API.
 31    
 32    This class provides methods for authentication, device management, and accessing
 33    various Wyze device services including:
 34    
 35    * **Bulbs** - Control brightness, color, and power state
 36    * **Switches** - Toggle power and monitor usage
 37    * **Cameras** - Access video streams and control settings
 38    * **Thermostats** - Manage temperature settings and modes
 39    * **Locks** - Control and monitor door locks
 40    * **Sensors** - Monitor motion, contact, and environmental sensors
 41    * **HMS** - Manage home monitoring system
 42    
 43    Most interactions with Wyze devices should go through this class.
 44    """
 45    # _client: Client
 46    _auth_lib: WyzeAuthLib
 47
 48    def __init__(self):
 49        self._bulb_service = None
 50        self._switch_service = None
 51        self._camera_service = None
 52        self._thermostat_service = None
 53        self._hms_service = None
 54        self._lock_service = None
 55        self._sensor_service = None
 56        self._wall_switch_service = None
 57        self._switch_usage_service = None
 58        self._email = None
 59        self._password = None
 60        self._key_id = None
 61        self._api_key = None
 62        self._service: Optional[BaseService] = None
 63        self._token_callbacks: List[Callable] = []
 64
 65    @classmethod
 66    async def create(cls):
 67        """
 68        Creates and initializes the Wyzeapy class asynchronously.
 69        
 70        This factory method provides a way to instantiate the class using async/await syntax,
 71        though it's currently a simple implementation that may be expanded in the future.
 72        
 73        **Returns:**
 74            `Wyzeapy`: A new instance of the Wyzeapy class ready for authentication.
 75        """
 76        self = cls()
 77        return self
 78
 79    async def login(
 80        self, email, password, key_id, api_key, token: Optional[Token] = None
 81    ):
 82        """
 83        Authenticates with the Wyze API and retrieves the user's access token.
 84        
 85        This method handles the authentication process, including token management
 86        and service initialization. If two-factor authentication is enabled on the account,
 87        it will raise an exception requiring the use of `login_with_2fa()` instead.
 88        
 89        **Args:**
 90        * `email` (str): User's email address for Wyze account
 91        * `password` (str): User's password for Wyze account
 92        * `key_id` (str): Key ID for third-party API access
 93        * `api_key` (str): API Key for third-party API access
 94        * `token` (Optional[Token], optional): Existing token from a previous session. Defaults to None.
 95        
 96        **Raises:**
 97        * `TwoFactorAuthenticationEnabled`: When the account has 2FA enabled and requires verification
 98        """
 99
100        self._email = email
101        self._password = password
102        self._key_id = key_id
103        self._api_key = api_key
104
105        try:
106            self._auth_lib = await WyzeAuthLib.create(
107                email, password, key_id, api_key, token, self.execute_token_callbacks
108            )
109            if token:
110                # User token supplied, refresh on startup
111                await self._auth_lib.refresh()
112            else:
113                await self._auth_lib.get_token_with_username_password(
114                    email, password, key_id, api_key
115                )
116            self._service = BaseService(self._auth_lib)
117        except TwoFactorAuthenticationEnabled as error:
118            raise error
119
120    async def login_with_2fa(self, verification_code) -> Token:
121        """
122        Completes the login process for accounts with two-factor authentication enabled.
123        
124        This method should be called after receiving a `TwoFactorAuthenticationEnabled`
125        exception from the `login()` method. It completes the authentication process
126        using the verification code sent to the user.
127        
128        **Args:**
129        * `verification_code` (str): The 2FA verification code received by the user
130            
131        **Returns:**
132        * `Token`: The authenticated user token object
133        """
134
135        _LOGGER.debug(f"Verification Code: {verification_code}")
136
137        await self._auth_lib.get_token_with_2fa(verification_code)
138        self._service = BaseService(self._auth_lib)
139        return self._auth_lib.token
140
141    async def execute_token_callbacks(self, token: Token):
142        """
143        Sends the token to all registered callback functions.
144        
145        This method is called internally whenever the token is refreshed or updated,
146        allowing external components to stay in sync with token changes.
147        
148        **Args:**
149        * `token` (Token): The current user token object
150        """
151        for callback in self._token_callbacks:
152            if iscoroutinefunction(callback):
153                await callback(token)
154            else:
155                callback(token)
156
157    def register_for_token_callback(self, callback_function):
158        """
159        Registers a callback function to be called whenever the user's token is modified.
160        
161        This allows external components to be notified of token changes for persistence
162        or other token-dependent operations.
163        
164        **Args:**
165        * `callback_function`: A function that accepts a Token object as its parameter
166        
167        **Example:**
168        ```python
169        def token_updated(token):
170            print(f"Token refreshed: {token.access_token[:10]}...")
171            
172        wyze = Wyzeapy()
173        wyze.register_for_token_callback(token_updated)
174        ```
175        """
176        self._token_callbacks.append(callback_function)
177
178    def unregister_for_token_callback(self, callback_function):
179        """
180        Removes a previously registered token callback function.
181        
182        This stops the specified callback from receiving token updates.
183        
184        **Args:**
185        * `callback_function`: The callback function to remove from the notification list
186        """
187        self._token_callbacks.remove(callback_function)
188
189    @property
190    async def unique_device_ids(self) -> Set[str]:
191        """
192        Retrieves a set of all unique device IDs known to the Wyze server.
193        
194        This property fetches all devices associated with the account and
195        extracts their MAC addresses as unique identifiers.
196        
197        **Returns:**
198        * `Set[str]`: A set containing all unique device IDs (MAC addresses)
199        
200        **Example:**
201        ```python
202        device_ids = await wyze.unique_device_ids
203        print(f"Found {len(device_ids)} devices")
204        ```
205        """
206
207        devices = await self._service.get_object_list()
208        device_ids = set()
209        for device in devices:
210            device_ids.add(device.mac)
211
212        return device_ids
213
214    @property
215    async def notifications_are_on(self) -> bool:
216        """
217        Checks if push notifications are enabled for the account.
218        
219        This property queries the user profile to determine the current
220        notification settings status.
221        
222        **Returns:**
223        * `bool`: True if notifications are enabled, False otherwise
224        """
225
226        response_json = await self._service.get_user_profile()
227        return response_json['data']['notification']
228
229    async def enable_notifications(self):
230        """Enables push notifications for the Wyze account.
231        
232        This method updates the user's profile to turn on push notifications
233        for all supported devices and events.
234        
235        **Example:**
236        ```python
237        # Turn on notifications
238        await wyze.enable_notifications()
239        ```
240        """
241
242        await self._service.set_push_info(True)
243
244    async def disable_notifications(self):
245        """Disables push notifications for the Wyze account.
246        
247        This method updates the user's profile to turn off push notifications
248        for all devices and events.
249        
250        **Example:**
251        ```python
252        # Turn off notifications
253        await wyze.disable_notifications()
254        ```
255        """
256
257        await self._service.set_push_info(False)
258
259    @classmethod
260    async def valid_login(
261        cls, email: str, password: str, key_id: str, api_key: str
262    ) -> bool:
263        """
264        Validates if the provided credentials can successfully authenticate with the Wyze API.
265        
266        This method attempts to log in with the provided credentials and returns whether
267        the authentication was successful. It's useful for validating credentials without
268        needing to handle the full login process.
269        
270        **Args:**
271        * `email` (str): The user's email address
272        * `password` (str): The user's password
273        * `key_id` (str): Key ID for third-party API access
274        * `api_key` (str): API Key for third-party API access
275            
276        **Returns:**
277        * `bool`: True if the credentials are valid and authentication succeeded
278        
279        **Example:**
280        ```python
281        is_valid = await Wyzeapy.valid_login("user@example.com", "password123", "key_id", "api_key")
282        if is_valid:
283            print("Credentials are valid")
284        else:
285            print("Invalid credentials")
286        ```
287        """
288
289        self = cls()
290        await self.login(email, password, key_id, api_key)
291
292        return not self._auth_lib.should_refresh
293
294    @property
295    async def bulb_service(self) -> BulbService:
296        """Provides access to the Wyze Bulb service.
297        
298        This property lazily initializes and returns a BulbService instance
299        for controlling and monitoring Wyze bulbs.
300        
301        **Returns:**
302        * `BulbService`: An instance of the bulb service for interacting with Wyze bulbs
303        
304        **Example:**
305        ```python
306        # Get all bulbs
307        bulb_service = await wyze.bulb_service
308        bulbs = await bulb_service.get_bulbs()
309        ```
310        """
311
312        if self._bulb_service is None:
313            self._bulb_service = BulbService(self._auth_lib)
314        return self._bulb_service
315
316    @property
317    async def switch_service(self) -> SwitchService:
318        """Provides access to the Wyze Switch service.
319        
320        This property lazily initializes and returns a SwitchService instance
321        for controlling and monitoring Wyze plugs and switches.
322        
323        **Returns:**
324        * `SwitchService`: An instance of the switch service for interacting with Wyze switches
325        
326        **Example:**
327        ```python
328        # Get all switches
329        switch_service = await wyze.switch_service
330        switches = await switch_service.get_switches()
331        ```
332        """
333
334        if self._switch_service is None:
335            self._switch_service = SwitchService(self._auth_lib)
336        return self._switch_service
337
338    @property
339    async def camera_service(self) -> CameraService:
340        """Provides access to the Wyze Camera service.
341        
342        This property lazily initializes and returns a CameraService instance
343        for controlling and monitoring Wyze cameras.
344        
345        **Returns:**
346        * `CameraService`: An instance of the camera service for interacting with Wyze cameras
347        
348        **Example:**
349        ```python
350        # Get all cameras
351        camera_service = await wyze.camera_service
352        cameras = await camera_service.get_cameras()
353        ```
354        """
355
356        if self._camera_service is None:
357            self._camera_service = CameraService(self._auth_lib)
358        return self._camera_service
359
360    @property
361    async def thermostat_service(self) -> ThermostatService:
362        """Provides access to the Wyze Thermostat service.
363        
364        This property lazily initializes and returns a ThermostatService instance
365        for controlling and monitoring Wyze thermostats.
366        
367        **Returns:**
368        * `ThermostatService`: An instance of the thermostat service for interacting with Wyze thermostats
369        
370        **Example:**
371        ```python
372        # Get all thermostats
373        thermostat_service = await wyze.thermostat_service
374        thermostats = await thermostat_service.get_thermostats()
375        ```
376        """
377
378        if self._thermostat_service is None:
379            self._thermostat_service = ThermostatService(self._auth_lib)
380        return self._thermostat_service
381
382    @property
383    async def hms_service(self) -> HMSService:
384        """Provides access to the Wyze Home Monitoring Service (HMS).
385        
386        This property lazily initializes and returns an HMSService instance
387        for controlling and monitoring the Wyze home security system.
388        
389        **Returns:**
390        * `HMSService`: An instance of the HMS service for interacting with Wyze home monitoring
391        
392        **Example:**
393        ```python
394        # Get HMS status
395        hms_service = await wyze.hms_service
396        status = await hms_service.get_hms_status()
397        ```
398        """
399
400        if self._hms_service is None:
401            self._hms_service = await HMSService.create(self._auth_lib)
402        return self._hms_service
403
404    @property
405    async def lock_service(self) -> LockService:
406        """Provides access to the Wyze Lock service.
407        
408        This property lazily initializes and returns a LockService instance
409        for controlling and monitoring Wyze locks.
410        
411        **Returns:**
412        * `LockService`: An instance of the lock service for interacting with Wyze locks
413        
414        **Example:**
415        ```python
416        # Get all locks
417        lock_service = await wyze.lock_service
418        locks = await lock_service.get_locks()
419        ```
420        """
421
422        if self._lock_service is None:
423            self._lock_service = LockService(self._auth_lib)
424        return self._lock_service
425
426    @property
427    async def sensor_service(self) -> SensorService:
428        """Provides access to the Wyze Sensor service.
429        
430        This property lazily initializes and returns a SensorService instance
431        for monitoring Wyze sensors such as contact sensors, motion sensors, etc.
432        
433        **Returns:**
434        * `SensorService`: An instance of the sensor service for interacting with Wyze sensors
435        
436        **Example:**
437        ```python
438        # Get all sensors
439        sensor_service = await wyze.sensor_service
440        sensors = await sensor_service.get_sensors()
441        ```
442        """
443
444        if self._sensor_service is None:
445            self._sensor_service = SensorService(self._auth_lib)
446        return self._sensor_service
447
448    @property
449    async def wall_switch_service(self) -> WallSwitchService:
450        """Provides access to the Wyze Wall Switch service.
451        
452        This property lazily initializes and returns a WallSwitchService instance
453        for controlling and monitoring Wyze wall switches.
454        
455        **Returns:**
456        * `WallSwitchService`: An instance of the wall switch service for interacting with Wyze wall switches
457        
458        **Example:**
459        ```python
460        # Get all wall switches
461        wall_switch_service = await wyze.wall_switch_service
462        switches = await wall_switch_service.get_wall_switches()
463        ```
464        """
465
466        if self._wall_switch_service is None:
467            self._wall_switch_service = WallSwitchService(self._auth_lib)
468        return self._wall_switch_service
469
470    @property
471    async def switch_usage_service(self) -> SwitchUsageService:
472        """Provides access to the Wyze Switch Usage service.
473        
474        This property lazily initializes and returns a SwitchUsageService instance
475        for retrieving usage statistics from Wyze switches and plugs.
476        
477        **Returns:**
478        * `SwitchUsageService`: An instance of the switch usage service for accessing Wyze switch usage data
479        
480        **Example:**
481        ```python
482        # Get usage data for a switch
483        usage_service = await wyze.switch_usage_service
484        usage = await usage_service.get_usage_records(switch_mac)
485        ```
486        """
487        if self._switch_usage_service is None:
488            self._switch_usage_service = SwitchUsageService(self._auth_lib)
489        return self._switch_usage_service

A Python module to assist developers in interacting with the Wyze service API.

This class provides methods for authentication, device management, and accessing various Wyze device services including:

  • Bulbs - Control brightness, color, and power state
  • Switches - Toggle power and monitor usage
  • Cameras - Access video streams and control settings
  • Thermostats - Manage temperature settings and modes
  • Locks - Control and monitor door locks
  • Sensors - Monitor motion, contact, and environmental sensors
  • HMS - Manage home monitoring system

Most interactions with Wyze devices should go through this class.

@classmethod
async def create(cls):
65    @classmethod
66    async def create(cls):
67        """
68        Creates and initializes the Wyzeapy class asynchronously.
69        
70        This factory method provides a way to instantiate the class using async/await syntax,
71        though it's currently a simple implementation that may be expanded in the future.
72        
73        **Returns:**
74            `Wyzeapy`: A new instance of the Wyzeapy class ready for authentication.
75        """
76        self = cls()
77        return self

Creates and initializes the Wyzeapy class asynchronously.

This factory method provides a way to instantiate the class using async/await syntax, though it's currently a simple implementation that may be expanded in the future.

Returns: Wyzeapy: A new instance of the Wyzeapy class ready for authentication.

async def login( self, email, password, key_id, api_key, token: Optional[wyzeapy.wyze_auth_lib.Token] = None):
 79    async def login(
 80        self, email, password, key_id, api_key, token: Optional[Token] = None
 81    ):
 82        """
 83        Authenticates with the Wyze API and retrieves the user's access token.
 84        
 85        This method handles the authentication process, including token management
 86        and service initialization. If two-factor authentication is enabled on the account,
 87        it will raise an exception requiring the use of `login_with_2fa()` instead.
 88        
 89        **Args:**
 90        * `email` (str): User's email address for Wyze account
 91        * `password` (str): User's password for Wyze account
 92        * `key_id` (str): Key ID for third-party API access
 93        * `api_key` (str): API Key for third-party API access
 94        * `token` (Optional[Token], optional): Existing token from a previous session. Defaults to None.
 95        
 96        **Raises:**
 97        * `TwoFactorAuthenticationEnabled`: When the account has 2FA enabled and requires verification
 98        """
 99
100        self._email = email
101        self._password = password
102        self._key_id = key_id
103        self._api_key = api_key
104
105        try:
106            self._auth_lib = await WyzeAuthLib.create(
107                email, password, key_id, api_key, token, self.execute_token_callbacks
108            )
109            if token:
110                # User token supplied, refresh on startup
111                await self._auth_lib.refresh()
112            else:
113                await self._auth_lib.get_token_with_username_password(
114                    email, password, key_id, api_key
115                )
116            self._service = BaseService(self._auth_lib)
117        except TwoFactorAuthenticationEnabled as error:
118            raise error

Authenticates with the Wyze API and retrieves the user's access token.

This method handles the authentication process, including token management and service initialization. If two-factor authentication is enabled on the account, it will raise an exception requiring the use of login_with_2fa() instead.

Args:

  • email (str): User's email address for Wyze account
  • password (str): User's password for Wyze account
  • key_id (str): Key ID for third-party API access
  • api_key (str): API Key for third-party API access
  • token (Optional[Token], optional): Existing token from a previous session. Defaults to None.

Raises:

  • TwoFactorAuthenticationEnabled: When the account has 2FA enabled and requires verification
async def login_with_2fa(self, verification_code) -> wyzeapy.wyze_auth_lib.Token:
120    async def login_with_2fa(self, verification_code) -> Token:
121        """
122        Completes the login process for accounts with two-factor authentication enabled.
123        
124        This method should be called after receiving a `TwoFactorAuthenticationEnabled`
125        exception from the `login()` method. It completes the authentication process
126        using the verification code sent to the user.
127        
128        **Args:**
129        * `verification_code` (str): The 2FA verification code received by the user
130            
131        **Returns:**
132        * `Token`: The authenticated user token object
133        """
134
135        _LOGGER.debug(f"Verification Code: {verification_code}")
136
137        await self._auth_lib.get_token_with_2fa(verification_code)
138        self._service = BaseService(self._auth_lib)
139        return self._auth_lib.token

Completes the login process for accounts with two-factor authentication enabled.

This method should be called after receiving a TwoFactorAuthenticationEnabled exception from the login() method. It completes the authentication process using the verification code sent to the user.

Args:

  • verification_code (str): The 2FA verification code received by the user

Returns:

  • Token: The authenticated user token object
async def execute_token_callbacks(self, token: wyzeapy.wyze_auth_lib.Token):
141    async def execute_token_callbacks(self, token: Token):
142        """
143        Sends the token to all registered callback functions.
144        
145        This method is called internally whenever the token is refreshed or updated,
146        allowing external components to stay in sync with token changes.
147        
148        **Args:**
149        * `token` (Token): The current user token object
150        """
151        for callback in self._token_callbacks:
152            if iscoroutinefunction(callback):
153                await callback(token)
154            else:
155                callback(token)

Sends the token to all registered callback functions.

This method is called internally whenever the token is refreshed or updated, allowing external components to stay in sync with token changes.

Args:

  • token (Token): The current user token object
def register_for_token_callback(self, callback_function):
157    def register_for_token_callback(self, callback_function):
158        """
159        Registers a callback function to be called whenever the user's token is modified.
160        
161        This allows external components to be notified of token changes for persistence
162        or other token-dependent operations.
163        
164        **Args:**
165        * `callback_function`: A function that accepts a Token object as its parameter
166        
167        **Example:**
168        ```python
169        def token_updated(token):
170            print(f"Token refreshed: {token.access_token[:10]}...")
171            
172        wyze = Wyzeapy()
173        wyze.register_for_token_callback(token_updated)
174        ```
175        """
176        self._token_callbacks.append(callback_function)

Registers a callback function to be called whenever the user's token is modified.

This allows external components to be notified of token changes for persistence or other token-dependent operations.

Args:

  • callback_function: A function that accepts a Token object as its parameter

Example:

def token_updated(token):
    print(f"Token refreshed: {token.access_token[:10]}...")

wyze = Wyzeapy()
wyze.register_for_token_callback(token_updated)
def unregister_for_token_callback(self, callback_function):
178    def unregister_for_token_callback(self, callback_function):
179        """
180        Removes a previously registered token callback function.
181        
182        This stops the specified callback from receiving token updates.
183        
184        **Args:**
185        * `callback_function`: The callback function to remove from the notification list
186        """
187        self._token_callbacks.remove(callback_function)

Removes a previously registered token callback function.

This stops the specified callback from receiving token updates.

Args:

  • callback_function: The callback function to remove from the notification list
unique_device_ids: Set[str]
189    @property
190    async def unique_device_ids(self) -> Set[str]:
191        """
192        Retrieves a set of all unique device IDs known to the Wyze server.
193        
194        This property fetches all devices associated with the account and
195        extracts their MAC addresses as unique identifiers.
196        
197        **Returns:**
198        * `Set[str]`: A set containing all unique device IDs (MAC addresses)
199        
200        **Example:**
201        ```python
202        device_ids = await wyze.unique_device_ids
203        print(f"Found {len(device_ids)} devices")
204        ```
205        """
206
207        devices = await self._service.get_object_list()
208        device_ids = set()
209        for device in devices:
210            device_ids.add(device.mac)
211
212        return device_ids

Retrieves a set of all unique device IDs known to the Wyze server.

This property fetches all devices associated with the account and extracts their MAC addresses as unique identifiers.

Returns:

  • Set[str]: A set containing all unique device IDs (MAC addresses)

Example:

device_ids = await wyze.unique_device_ids
print(f"Found {len(device_ids)} devices")
notifications_are_on: bool
214    @property
215    async def notifications_are_on(self) -> bool:
216        """
217        Checks if push notifications are enabled for the account.
218        
219        This property queries the user profile to determine the current
220        notification settings status.
221        
222        **Returns:**
223        * `bool`: True if notifications are enabled, False otherwise
224        """
225
226        response_json = await self._service.get_user_profile()
227        return response_json['data']['notification']

Checks if push notifications are enabled for the account.

This property queries the user profile to determine the current notification settings status.

Returns:

  • bool: True if notifications are enabled, False otherwise
async def enable_notifications(self):
229    async def enable_notifications(self):
230        """Enables push notifications for the Wyze account.
231        
232        This method updates the user's profile to turn on push notifications
233        for all supported devices and events.
234        
235        **Example:**
236        ```python
237        # Turn on notifications
238        await wyze.enable_notifications()
239        ```
240        """
241
242        await self._service.set_push_info(True)

Enables push notifications for the Wyze account.

This method updates the user's profile to turn on push notifications for all supported devices and events.

Example:

# Turn on notifications
await wyze.enable_notifications()
async def disable_notifications(self):
244    async def disable_notifications(self):
245        """Disables push notifications for the Wyze account.
246        
247        This method updates the user's profile to turn off push notifications
248        for all devices and events.
249        
250        **Example:**
251        ```python
252        # Turn off notifications
253        await wyze.disable_notifications()
254        ```
255        """
256
257        await self._service.set_push_info(False)

Disables push notifications for the Wyze account.

This method updates the user's profile to turn off push notifications for all devices and events.

Example:

# Turn off notifications
await wyze.disable_notifications()
@classmethod
async def valid_login(cls, email: str, password: str, key_id: str, api_key: str) -> bool:
259    @classmethod
260    async def valid_login(
261        cls, email: str, password: str, key_id: str, api_key: str
262    ) -> bool:
263        """
264        Validates if the provided credentials can successfully authenticate with the Wyze API.
265        
266        This method attempts to log in with the provided credentials and returns whether
267        the authentication was successful. It's useful for validating credentials without
268        needing to handle the full login process.
269        
270        **Args:**
271        * `email` (str): The user's email address
272        * `password` (str): The user's password
273        * `key_id` (str): Key ID for third-party API access
274        * `api_key` (str): API Key for third-party API access
275            
276        **Returns:**
277        * `bool`: True if the credentials are valid and authentication succeeded
278        
279        **Example:**
280        ```python
281        is_valid = await Wyzeapy.valid_login("user@example.com", "password123", "key_id", "api_key")
282        if is_valid:
283            print("Credentials are valid")
284        else:
285            print("Invalid credentials")
286        ```
287        """
288
289        self = cls()
290        await self.login(email, password, key_id, api_key)
291
292        return not self._auth_lib.should_refresh

Validates if the provided credentials can successfully authenticate with the Wyze API.

This method attempts to log in with the provided credentials and returns whether the authentication was successful. It's useful for validating credentials without needing to handle the full login process.

Args:

  • email (str): The user's email address
  • password (str): The user's password
  • key_id (str): Key ID for third-party API access
  • api_key (str): API Key for third-party API access

Returns:

  • bool: True if the credentials are valid and authentication succeeded

Example:

is_valid = await Wyzeapy.valid_login("user@example.com", "password123", "key_id", "api_key")
if is_valid:
    print("Credentials are valid")
else:
    print("Invalid credentials")
bulb_service: wyzeapy.services.bulb_service.BulbService
294    @property
295    async def bulb_service(self) -> BulbService:
296        """Provides access to the Wyze Bulb service.
297        
298        This property lazily initializes and returns a BulbService instance
299        for controlling and monitoring Wyze bulbs.
300        
301        **Returns:**
302        * `BulbService`: An instance of the bulb service for interacting with Wyze bulbs
303        
304        **Example:**
305        ```python
306        # Get all bulbs
307        bulb_service = await wyze.bulb_service
308        bulbs = await bulb_service.get_bulbs()
309        ```
310        """
311
312        if self._bulb_service is None:
313            self._bulb_service = BulbService(self._auth_lib)
314        return self._bulb_service

Provides access to the Wyze Bulb service.

This property lazily initializes and returns a BulbService instance for controlling and monitoring Wyze bulbs.

Returns:

  • BulbService: An instance of the bulb service for interacting with Wyze bulbs

Example:

# Get all bulbs
bulb_service = await wyze.bulb_service
bulbs = await bulb_service.get_bulbs()
switch_service: wyzeapy.services.switch_service.SwitchService
316    @property
317    async def switch_service(self) -> SwitchService:
318        """Provides access to the Wyze Switch service.
319        
320        This property lazily initializes and returns a SwitchService instance
321        for controlling and monitoring Wyze plugs and switches.
322        
323        **Returns:**
324        * `SwitchService`: An instance of the switch service for interacting with Wyze switches
325        
326        **Example:**
327        ```python
328        # Get all switches
329        switch_service = await wyze.switch_service
330        switches = await switch_service.get_switches()
331        ```
332        """
333
334        if self._switch_service is None:
335            self._switch_service = SwitchService(self._auth_lib)
336        return self._switch_service

Provides access to the Wyze Switch service.

This property lazily initializes and returns a SwitchService instance for controlling and monitoring Wyze plugs and switches.

Returns:

  • SwitchService: An instance of the switch service for interacting with Wyze switches

Example:

# Get all switches
switch_service = await wyze.switch_service
switches = await switch_service.get_switches()
camera_service: wyzeapy.services.camera_service.CameraService
338    @property
339    async def camera_service(self) -> CameraService:
340        """Provides access to the Wyze Camera service.
341        
342        This property lazily initializes and returns a CameraService instance
343        for controlling and monitoring Wyze cameras.
344        
345        **Returns:**
346        * `CameraService`: An instance of the camera service for interacting with Wyze cameras
347        
348        **Example:**
349        ```python
350        # Get all cameras
351        camera_service = await wyze.camera_service
352        cameras = await camera_service.get_cameras()
353        ```
354        """
355
356        if self._camera_service is None:
357            self._camera_service = CameraService(self._auth_lib)
358        return self._camera_service

Provides access to the Wyze Camera service.

This property lazily initializes and returns a CameraService instance for controlling and monitoring Wyze cameras.

Returns:

  • CameraService: An instance of the camera service for interacting with Wyze cameras

Example:

# Get all cameras
camera_service = await wyze.camera_service
cameras = await camera_service.get_cameras()
360    @property
361    async def thermostat_service(self) -> ThermostatService:
362        """Provides access to the Wyze Thermostat service.
363        
364        This property lazily initializes and returns a ThermostatService instance
365        for controlling and monitoring Wyze thermostats.
366        
367        **Returns:**
368        * `ThermostatService`: An instance of the thermostat service for interacting with Wyze thermostats
369        
370        **Example:**
371        ```python
372        # Get all thermostats
373        thermostat_service = await wyze.thermostat_service
374        thermostats = await thermostat_service.get_thermostats()
375        ```
376        """
377
378        if self._thermostat_service is None:
379            self._thermostat_service = ThermostatService(self._auth_lib)
380        return self._thermostat_service

Provides access to the Wyze Thermostat service.

This property lazily initializes and returns a ThermostatService instance for controlling and monitoring Wyze thermostats.

Returns:

  • ThermostatService: An instance of the thermostat service for interacting with Wyze thermostats

Example:

# Get all thermostats
thermostat_service = await wyze.thermostat_service
thermostats = await thermostat_service.get_thermostats()
hms_service: wyzeapy.services.hms_service.HMSService
382    @property
383    async def hms_service(self) -> HMSService:
384        """Provides access to the Wyze Home Monitoring Service (HMS).
385        
386        This property lazily initializes and returns an HMSService instance
387        for controlling and monitoring the Wyze home security system.
388        
389        **Returns:**
390        * `HMSService`: An instance of the HMS service for interacting with Wyze home monitoring
391        
392        **Example:**
393        ```python
394        # Get HMS status
395        hms_service = await wyze.hms_service
396        status = await hms_service.get_hms_status()
397        ```
398        """
399
400        if self._hms_service is None:
401            self._hms_service = await HMSService.create(self._auth_lib)
402        return self._hms_service

Provides access to the Wyze Home Monitoring Service (HMS).

This property lazily initializes and returns an HMSService instance for controlling and monitoring the Wyze home security system.

Returns:

  • HMSService: An instance of the HMS service for interacting with Wyze home monitoring

Example:

# Get HMS status
hms_service = await wyze.hms_service
status = await hms_service.get_hms_status()
lock_service: wyzeapy.services.lock_service.LockService
404    @property
405    async def lock_service(self) -> LockService:
406        """Provides access to the Wyze Lock service.
407        
408        This property lazily initializes and returns a LockService instance
409        for controlling and monitoring Wyze locks.
410        
411        **Returns:**
412        * `LockService`: An instance of the lock service for interacting with Wyze locks
413        
414        **Example:**
415        ```python
416        # Get all locks
417        lock_service = await wyze.lock_service
418        locks = await lock_service.get_locks()
419        ```
420        """
421
422        if self._lock_service is None:
423            self._lock_service = LockService(self._auth_lib)
424        return self._lock_service

Provides access to the Wyze Lock service.

This property lazily initializes and returns a LockService instance for controlling and monitoring Wyze locks.

Returns:

  • LockService: An instance of the lock service for interacting with Wyze locks

Example:

# Get all locks
lock_service = await wyze.lock_service
locks = await lock_service.get_locks()
sensor_service: wyzeapy.services.sensor_service.SensorService
426    @property
427    async def sensor_service(self) -> SensorService:
428        """Provides access to the Wyze Sensor service.
429        
430        This property lazily initializes and returns a SensorService instance
431        for monitoring Wyze sensors such as contact sensors, motion sensors, etc.
432        
433        **Returns:**
434        * `SensorService`: An instance of the sensor service for interacting with Wyze sensors
435        
436        **Example:**
437        ```python
438        # Get all sensors
439        sensor_service = await wyze.sensor_service
440        sensors = await sensor_service.get_sensors()
441        ```
442        """
443
444        if self._sensor_service is None:
445            self._sensor_service = SensorService(self._auth_lib)
446        return self._sensor_service

Provides access to the Wyze Sensor service.

This property lazily initializes and returns a SensorService instance for monitoring Wyze sensors such as contact sensors, motion sensors, etc.

Returns:

  • SensorService: An instance of the sensor service for interacting with Wyze sensors

Example:

# Get all sensors
sensor_service = await wyze.sensor_service
sensors = await sensor_service.get_sensors()
448    @property
449    async def wall_switch_service(self) -> WallSwitchService:
450        """Provides access to the Wyze Wall Switch service.
451        
452        This property lazily initializes and returns a WallSwitchService instance
453        for controlling and monitoring Wyze wall switches.
454        
455        **Returns:**
456        * `WallSwitchService`: An instance of the wall switch service for interacting with Wyze wall switches
457        
458        **Example:**
459        ```python
460        # Get all wall switches
461        wall_switch_service = await wyze.wall_switch_service
462        switches = await wall_switch_service.get_wall_switches()
463        ```
464        """
465
466        if self._wall_switch_service is None:
467            self._wall_switch_service = WallSwitchService(self._auth_lib)
468        return self._wall_switch_service

Provides access to the Wyze Wall Switch service.

This property lazily initializes and returns a WallSwitchService instance for controlling and monitoring Wyze wall switches.

Returns:

  • WallSwitchService: An instance of the wall switch service for interacting with Wyze wall switches

Example:

# Get all wall switches
wall_switch_service = await wyze.wall_switch_service
switches = await wall_switch_service.get_wall_switches()
switch_usage_service: wyzeapy.services.switch_service.SwitchUsageService
470    @property
471    async def switch_usage_service(self) -> SwitchUsageService:
472        """Provides access to the Wyze Switch Usage service.
473        
474        This property lazily initializes and returns a SwitchUsageService instance
475        for retrieving usage statistics from Wyze switches and plugs.
476        
477        **Returns:**
478        * `SwitchUsageService`: An instance of the switch usage service for accessing Wyze switch usage data
479        
480        **Example:**
481        ```python
482        # Get usage data for a switch
483        usage_service = await wyze.switch_usage_service
484        usage = await usage_service.get_usage_records(switch_mac)
485        ```
486        """
487        if self._switch_usage_service is None:
488            self._switch_usage_service = SwitchUsageService(self._auth_lib)
489        return self._switch_usage_service

Provides access to the Wyze Switch Usage service.

This property lazily initializes and returns a SwitchUsageService instance for retrieving usage statistics from Wyze switches and plugs.

Returns:

  • SwitchUsageService: An instance of the switch usage service for accessing Wyze switch usage data

Example:

# Get usage data for a switch
usage_service = await wyze.switch_usage_service
usage = await usage_service.get_usage_records(switch_mac)