Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

66 support for wyze switch #104

Merged
merged 2 commits into from
Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ Whether you're building a custom app, or integrating into a third-party service

The **Python Wyze SDK** allows interaction with:

- `wyze_sdk.client.bulbs`: for controlling Wyze Bulb and Wyze Bulb Color
- `wyze_sdk.client.bulbs`: for controlling Wyze Bulb, Wyze Bulb Color, Wyze Bulb White, and Wyze Light Strip
- `wyze_sdk.client.entry_sensors`: for interacting with Wyze Entry Sensor
- `wyze_sdk.client.cameras`: for interacting with Wyze Cameras
- `wyze_sdk.client.events`: for managing Wyze alarm events
- `wyze_sdk.client.locks`: for interacting with Wyze Lock
- `wyze_sdk.client.locks`: for interacting with Wyze Lock and Wyze Lock Keypad
- `wyze_sdk.client.motion_sensors`: for interacting with Wyze Motion Sensor
- `wyze_sdk.client.plugs`: for controlling Wyze Plug and Wyze Plug Outdoor
- `wyze_sdk.client.scales`: for controlling Wyze Scale
- `wyze_sdk.client.thermostats`: for controlling Wyze Thermostat
- `wyze_sdk.client.switches`: for controlling Wyze Switch
- `wyze_sdk.client.thermostats`: for controlling Wyze Thermostat and Wyze Room Sensor
- `wyze_sdk.client.vacuums`: for controlling Wyze Robot Vacuum

**Disclaimer: This repository is for non-destructive use only. WyzeLabs is a wonderful company providing excellent devices at a reasonable price. I ask that you do no harm and be civilized.**
Expand Down
8 changes: 5 additions & 3 deletions docs-src/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,25 @@ Whether you're building a custom app, or integrating into a third-party service
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Web Client | Send data to or query data from Wyze using a variety of device-specific sub-clients. | ``wyze_sdk.client`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Bulb Client | Control Wyze Bulb and Wyze Bulb Color | ``wyze_sdk.client.bulbs`` |
| Bulb Client | Control Wyze Bulb, Wyze Bulb Color, Wyze Bulb White, and Wyze Light Strip | ``wyze_sdk.client.bulbs`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Entry Sensor Client | Interact with Wyze Sense Entry Sensors | ``wyze_sdk.client.entry_sensors`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Camera Client | Control Wyze Cameras | ``wyze_sdk.client.cameras`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Event Client | Manage Wyze alarm events | ``wyze_sdk.client.events`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Lock Client | Control Wyze Lock | ``wyze_sdk.client.locks`` |
| Lock Client | Control Wyze Lock and Wyze Lock Keypad | ``wyze_sdk.client.locks`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Motion Sensor Client | Interact with Wyze Sense Motion Sensors | ``wyze_sdk.client.motion_sensors`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Plug Client | Control Wyze Plug and Wyze Plug Outdoor | ``wyze_sdk.client.plugs`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Scale Client | Control Wyze Scale | ``wyze_sdk.client.scales`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Thermostat Client | Control Wyze Thermostat | ``wyze_sdk.client.thermostats`` |
| Switch Client | Control Wyze Switch | ``wyze_sdk.client.switches`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Thermostat Client | Control Wyze Thermostat and Wyze Room Sensor | ``wyze_sdk.client.thermostats`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
| Vacuum Client | Control Wyze Robot Vacuum | ``wyze_sdk.client.vacuums`` |
+--------------------------------+-----------------------------------------------------------------------------------------------+------------------------------------+
Expand Down
8 changes: 8 additions & 0 deletions docs-src/wyze_sdk.api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ Entry Sensors
:inherited-members:
:show-inheritance:

Switches
===========
.. autoclass:: wyze_sdk.api.devices.switches.SwitchesClient
:members:
:undoc-members:
:inherited-members:
:show-inheritance:

Thermostats
===========
.. autoclass:: wyze_sdk.api.devices.thermostats.ThermostatsClient
Expand Down
16 changes: 16 additions & 0 deletions docs-src/wyze_sdk.models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ wyze\_sdk.models.devices.cameras
:undoc-members:
:inherited-members:

wyze\_sdk.models.devices.lights
------------------------------

.. automodule:: wyze_sdk.models.devices.lights
:members:
:undoc-members:
:inherited-members:

wyze\_sdk.models.devices.locks
------------------------------

Expand Down Expand Up @@ -66,6 +74,14 @@ wyze\_sdk.models.devices.sensors
:undoc-members:
:inherited-members:

wyze\_sdk.models.devices.switches
------------------------------------

.. automodule:: wyze_sdk.models.devices.switches
:members:
:undoc-members:
:inherited-members:

wyze\_sdk.models.devices.thermostats
------------------------------------

Expand Down
8 changes: 8 additions & 0 deletions docs-src/wyze_sdk.service.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ wyze\_sdk.service.scale\_service module
:undoc-members:
:show-inheritance:

wyze\_sdk.service.sirius\_service module
---------------------------------------

.. automodule:: wyze_sdk.service.sirius_service
:members:
:undoc-members:
:show-inheritance:

wyze\_sdk.service.venus\_service module
---------------------------------------

Expand Down
7 changes: 5 additions & 2 deletions wyze_sdk/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from wyze_sdk.errors import WyzeClientConfigurationError
from wyze_sdk.service import (ApiServiceClient, EarthServiceClient,
GeneralApiServiceClient, PlatformServiceClient,
ScaleServiceClient, VenusServiceClient,
WyzeResponse)
ScaleServiceClient, SiriusServiceClient,
VenusServiceClient, WyzeResponse)


class BaseClient(object, metaclass=ABCMeta):
Expand Down Expand Up @@ -63,6 +63,9 @@ def _general_api_client(self) -> GeneralApiServiceClient:
def _scale_client(self) -> ScaleServiceClient:
return BaseClient._service_client(ScaleServiceClient, token=self._token, base_url=self._base_url)

def _sirius_client(self) -> EarthServiceClient:
return BaseClient._service_client(SiriusServiceClient, token=self._token, base_url=self._base_url)

def _venus_client(self) -> VenusServiceClient:
return BaseClient._service_client(VenusServiceClient, token=self._token, base_url=self._base_url)

Expand Down
6 changes: 5 additions & 1 deletion wyze_sdk/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
ContactSensorsClient, LocksClient,
MotionSensorsClient, PlugsClient,
ScalesClient, ThermostatsClient,
VacuumsClient)
SwitchesClient, VacuumsClient)
from wyze_sdk.api.events import EventsClient
from wyze_sdk.errors import WyzeClientConfigurationError
from wyze_sdk.models.devices import Device, DeviceParser
Expand Down Expand Up @@ -94,6 +94,10 @@ def locks(self) -> LocksClient:
def scales(self) -> ScalesClient:
return ScalesClient(token=self._token, user_id=self._user_id, base_url=self._base_url)

@property
def switches(self) -> SwitchesClient:
return SwitchesClient(token=self._token, base_url=self._base_url)

@property
def events(self) -> EventsClient:
return EventsClient(token=self._token, base_url=self._base_url)
Expand Down
1 change: 1 addition & 0 deletions wyze_sdk/api/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
from .plugs import PlugsClient # noqa
from .scales import ScalesClient # noqa
from .sensors import ContactSensorsClient, MotionSensorsClient # noqa
from .switches import SwitchesClient # noqa
from .thermostats import ThermostatsClient # noqa
from .vacuums import VacuumsClient # noqa
104 changes: 104 additions & 0 deletions wyze_sdk/api/devices/switches.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from datetime import datetime, timedelta
from typing import Optional, Sequence, Tuple, Union

from wyze_sdk.api.base import BaseClient
from wyze_sdk.models import JsonObject
from wyze_sdk.models.devices import DeviceModels, DeviceProp
from wyze_sdk.models.devices.switches import (Switch, SwitchProps, SwitchTimerAction, SwitchTimerActionType)
from wyze_sdk.service import WyzeResponse


class SwitchesClient(BaseClient):
"""A Client that services Wyze switches.
"""

def list(self, **kwargs) -> Sequence[Switch]:
"""Lists all switches available to a Wyze account.

:rtype: Sequence[Switch]
"""
return [Switch(**device) for device in self._list_switches()]

def _list_switches(self, **kwargs) -> Sequence[dict]:
return [device for device in super()._list_devices() if device["product_model"] in DeviceModels.SWITCH]

def info(self, *, device_mac: str, **kwargs) -> Optional[Switch]:
"""Retrieves details of a switch.

:param str device_mac: The device mac. e.g. ``LD_SS1_ABCDEF1234567890``

:rtype: Optional[Switch]
"""
switches = [_switch for _switch in self._list_switches() if _switch['mac'] == device_mac]
if len(switches) == 0:
return None

switch = switches[0]

iot_prop = super()._sirius_client().get_iot_prop(did=device_mac, keys=[prop_def.pid for prop_def in Switch.props().values()])
if "data" in iot_prop.data and "props" in iot_prop.data["data"]:
switch.update(iot_prop.data["data"]["props"])

return Switch(**switch)

def turn_on(self, *, device_mac: str, device_model: str, after: Optional[timedelta] = None, **kwargs) -> WyzeResponse:
"""Turns on a switch.

:param str device_mac: The device mac. e.g. ``LD_SS1_ABCDEF1234567890``
:param str device_model: The device model. e.g. ``LD_SS1``
:param Optional[timedelta] after: The delay before performing the action.

:rtype: WyzeResponse
"""
if after is None:
return self._set_switch_properties(device_mac, device_model, DeviceProp(definition=SwitchProps.switch_power(), value=True))

return self._set_switch_properties(device_mac, device_model, DeviceProp(definition=SwitchProps.timer_action(), value=[SwitchTimerAction(action=SwitchTimerActionType.TURN_ON, time=datetime.now() + after)]))

def turn_off(self, *, device_mac: str, device_model: str, after: Optional[timedelta] = None, **kwargs) -> WyzeResponse:
"""Turns off a switch.

:param str device_mac: The device mac. e.g. ``LD_SS1_ABCDEF1234567890``
:param str device_model: The device model. e.g. ``LD_SS1``
:param Optional[timedelta] after: The delay before performing the action.

:rtype: WyzeResponse
"""
if after is None:
return self._set_switch_properties(device_mac, device_model, DeviceProp(definition=SwitchProps.switch_power(), value=False))

return self._set_switch_properties(device_mac, device_model, DeviceProp(definition=SwitchProps.timer_action(), value=[SwitchTimerAction(action=SwitchTimerActionType.TURN_OFF, time=datetime.now() + after)]))

def clear_timer(self, *, device_mac: str, device_model: str, **kwargs) -> WyzeResponse:
"""Clears any existing power state timer on the switch.

:param str device_mac: The device mac. e.g. ``LD_SS1_ABCDEF1234567890``
:param str device_model: The device model. e.g. ``LD_SS1``
"""
return self._set_switch_properties(device_mac, device_model, DeviceProp(definition=SwitchProps.timer_action(), value=[]))

def set_away_mode(self, *, device_mac: str, device_model: str, away_mode: bool = True, **kwargs) -> WyzeResponse:
"""Sets away/vacation mode for a switch.

:param str device_mac: The device mac. e.g. ``LD_SS1_ABCDEF1234567890``
:param str device_model: The device model. e.g. ``LD_SS1``
:param bool away_mode: The new away mode. e.g. ``True``
"""
return self._set_switch_property(device_mac, device_model, DeviceProp(definition=SwitchProps.away_mode(), value=away_mode))

def _set_switch_property(self, device_mac: str, device_model: str, prop: DeviceProp) -> WyzeResponse:
return super()._sirius_client().set_iot_prop(did=device_mac, model=device_model, key=prop.definition.pid, value=str(prop.api_value))

def _set_switch_properties(self, device_mac: str, device_model: str, props: Union[DeviceProp, Sequence[DeviceProp]]) -> WyzeResponse:
if not isinstance(props, (list, Tuple)):
props = [props]
the_props = {}
for prop in props:
if prop.definition.type == JsonObject:
the_props[prop.definition.pid] = prop.api_value.to_json()
elif prop.definition.type == Sequence:
the_props[prop.definition.pid] = [_prop.to_json() for _prop in prop.api_value if isinstance(_prop, JsonObject)]
else:
the_props[prop.definition.pid] = str(prop.api_value)
return super()._sirius_client().set_iot_prop_by_topic(
did=device_mac, model=device_model, props=the_props)
73 changes: 38 additions & 35 deletions wyze_sdk/models/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .plugs import OutdoorPlug, Plug, PlugProps # noqa
from .scales import Scale, ScaleProps, ScaleRecord, UserGoalWeight # noqa
from .sensors import ContactSensor, MotionSensor, Sensor, SensorProps # noqa
from .switches import Switch, SwitchProps # noqa
from .thermostats import (Thermostat, ThermostatFanMode, # noqa
ThermostatProps, ThermostatScenarioType,
ThermostatSystemMode)
Expand All @@ -31,40 +32,42 @@ class DeviceParser(object):
def parse(cls, device: Union[dict, "Device"]) -> Optional["Device"]:
if device is None:
return None
elif isinstance(device, Device):
if isinstance(device, Device):
return device
else:
if "product_type" in device:
type = device["product_type"]
if type == BaseStation.type:
return BaseStation(**device)
elif type == Bulb.type:
return Bulb(**device)
elif type == Camera.type:
return Camera(**device)
elif type == ContactSensor.type:
return ContactSensor(**device)
elif type == Lock.type:
return Lock(**device)
elif type == LockGateway.type:
return LockGateway(**device)
elif type == MeshBulb.type:
return MeshBulb(**device)
elif type == MotionSensor.type:
return MotionSensor(**device)
elif type == OutdoorPlug.type:
return OutdoorPlug(**device)
elif type == Plug.type:
return Plug(**device)
elif type == Scale.type or type in DeviceModels.SCALE:
return Scale(**device)
elif type == Thermostat.type or type in DeviceModels.THERMOSTAT:
return Thermostat(**device)
elif type == Vacuum.type or type in DeviceModels.VACUUM:
return Vacuum(**device)
else:
cls._logger.warning(f"Unknown device type detected ({device})")
return Device(**device)
if "product_type" in device:
type = device["product_type"]
if type == BaseStation.type:
return BaseStation(**device)
elif type == Bulb.type:
return Bulb(**device)
elif type == Camera.type:
return Camera(**device)
elif type == ContactSensor.type:
return ContactSensor(**device)
elif type == Lock.type:
return Lock(**device)
elif type == LockGateway.type:
return LockGateway(**device)
elif type == MeshBulb.type:
return MeshBulb(**device)
elif type == MotionSensor.type:
return MotionSensor(**device)
elif type == OutdoorPlug.type:
return OutdoorPlug(**device)
elif type == Plug.type:
return Plug(**device)
elif type == Scale.type or type in DeviceModels.SCALE:
return Scale(**device)
elif type == Thermostat.type or type in DeviceModels.THERMOSTAT:
return Thermostat(**device)
elif type == Vacuum.type or type in DeviceModels.VACUUM:
return Vacuum(**device)
if "product_model" in device:
model = device["product_model"]
if model in DeviceModels.SWITCH:
return Switch(**device)
else:
cls._logger.warning(f"Unknown device detected and skipped ({device})")
return None
cls._logger.warning(f"Unknown device type detected ({device})")
return Device(**device)
cls._logger.warning(f"Unknown device detected and skipped ({device})")
return None
2 changes: 2 additions & 0 deletions wyze_sdk/models/devices/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class DeviceModels(object):
OUTDOOR_PLUG = ['WLPPO', 'WLPPO-SUB']
PLUG = ['WLPP1', 'WLPP1CFH'] + OUTDOOR_PLUG

SWITCH = ['LD_SS1']


class Product(object):
"""
Expand Down
Loading