wyzeapy 0.5.28__py3-none-any.whl → 0.5.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wyzeapy/__init__.py +277 -45
- wyzeapy/const.py +9 -4
- wyzeapy/crypto.py +31 -2
- wyzeapy/exceptions.py +11 -8
- wyzeapy/payload_factory.py +205 -170
- wyzeapy/services/__init__.py +3 -0
- wyzeapy/services/base_service.py +406 -212
- wyzeapy/services/bulb_service.py +67 -63
- wyzeapy/services/camera_service.py +136 -50
- wyzeapy/services/hms_service.py +8 -17
- wyzeapy/services/irrigation_service.py +189 -0
- wyzeapy/services/lock_service.py +5 -3
- wyzeapy/services/sensor_service.py +32 -11
- wyzeapy/services/switch_service.py +6 -2
- wyzeapy/services/thermostat_service.py +29 -15
- wyzeapy/services/update_manager.py +38 -11
- wyzeapy/services/wall_switch_service.py +18 -8
- wyzeapy/tests/test_irrigation_service.py +536 -0
- wyzeapy/types.py +29 -12
- wyzeapy/utils.py +98 -17
- wyzeapy/wyze_auth_lib.py +195 -37
- wyzeapy-0.5.30.dist-info/METADATA +13 -0
- wyzeapy-0.5.30.dist-info/RECORD +24 -0
- {wyzeapy-0.5.28.dist-info → wyzeapy-0.5.30.dist-info}/WHEEL +1 -1
- wyzeapy/tests/test_bulb_service.py +0 -135
- wyzeapy/tests/test_camera_service.py +0 -180
- wyzeapy/tests/test_hms_service.py +0 -90
- wyzeapy/tests/test_lock_service.py +0 -114
- wyzeapy/tests/test_sensor_service.py +0 -159
- wyzeapy/tests/test_switch_service.py +0 -138
- wyzeapy/tests/test_thermostat_service.py +0 -136
- wyzeapy/tests/test_wall_switch_service.py +0 -161
- wyzeapy-0.5.28.dist-info/LICENSES/GPL-3.0-only.txt +0 -232
- wyzeapy-0.5.28.dist-info/METADATA +0 -16
- wyzeapy-0.5.28.dist-info/RECORD +0 -31
wyzeapy/services/bulb_service.py
CHANGED
|
@@ -15,55 +15,86 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class Bulb(Device):
|
|
18
|
+
"""Bulb class for interacting with Wyze bulbs."""
|
|
19
|
+
|
|
18
20
|
_brightness: int = 0
|
|
19
21
|
_color_temp: int = 1800
|
|
20
22
|
_color: Optional[str]
|
|
21
|
-
enr: str
|
|
22
|
-
|
|
23
|
-
on: bool = False
|
|
24
|
-
cloud_fallback = False
|
|
25
23
|
|
|
26
24
|
def __init__(self, dictionary: Dict[Any, Any]):
|
|
25
|
+
"""Initialize the Bulb class.
|
|
26
|
+
|
|
27
|
+
:param dictionary: Dictionary containing the device parameters.
|
|
28
|
+
"""
|
|
29
|
+
self.enr: str = ""
|
|
30
|
+
"""Encryption string"""
|
|
31
|
+
self.on: bool = False
|
|
32
|
+
"""Variable that stores the on/off state of the bulb"""
|
|
33
|
+
self.cloud_fallback: bool = False
|
|
34
|
+
"""Variable that stores the cloud fallback state of the bulb"""
|
|
27
35
|
super().__init__(dictionary)
|
|
28
36
|
|
|
29
37
|
self.ip = self.device_params["ip"]
|
|
38
|
+
"""IP address of the bulb"""
|
|
30
39
|
|
|
31
|
-
if
|
|
32
|
-
self.type is DeviceTypes.MESH_LIGHT
|
|
33
|
-
or self.type is DeviceTypes.LIGHTSTRIP
|
|
34
|
-
):
|
|
40
|
+
if self.type is DeviceTypes.MESH_LIGHT or self.type is DeviceTypes.LIGHTSTRIP:
|
|
35
41
|
self._color = "000000"
|
|
36
42
|
|
|
37
43
|
@property
|
|
38
44
|
def brightness(self) -> int:
|
|
45
|
+
"""Property that stores the brightness of the bulb
|
|
46
|
+
:return: Brightness of the bulb
|
|
47
|
+
"""
|
|
39
48
|
return self._brightness
|
|
40
49
|
|
|
41
50
|
@brightness.setter
|
|
42
51
|
def brightness(self, value: int) -> None:
|
|
52
|
+
"""Setter for the brightness property
|
|
53
|
+
:param value: Brightness of the bulb
|
|
54
|
+
"""
|
|
43
55
|
assert value <= 100
|
|
44
56
|
assert value >= 0
|
|
45
57
|
self._brightness = value
|
|
46
58
|
|
|
47
59
|
@property
|
|
48
60
|
def color_temp(self) -> int:
|
|
61
|
+
"""Property that stores the color temperature of the bulb
|
|
62
|
+
:return: Color temperature of the bulb
|
|
63
|
+
"""
|
|
49
64
|
return self._color_temp
|
|
50
65
|
|
|
51
66
|
@color_temp.setter
|
|
52
67
|
def color_temp(self, value: int) -> None:
|
|
68
|
+
"""Setter for the color temperature property
|
|
69
|
+
:param value: Color temperature of the bulb
|
|
70
|
+
"""
|
|
53
71
|
self._color_temp = value
|
|
54
72
|
|
|
55
73
|
@property
|
|
56
74
|
def color(self) -> Optional[str]:
|
|
75
|
+
"""Property that stores the color of the bulb
|
|
76
|
+
:return: Color of the bulb
|
|
77
|
+
"""
|
|
57
78
|
return self._color
|
|
58
79
|
|
|
59
80
|
@color.setter
|
|
60
81
|
def color(self, value) -> None:
|
|
82
|
+
"""Setter for the color property
|
|
83
|
+
:param value: Color of the bulb
|
|
84
|
+
"""
|
|
61
85
|
assert re.match(r"^([A-Fa-f\d]{6}|[A-Fa-f\d]{3})$", value) is not None
|
|
62
86
|
self._color = value
|
|
63
87
|
|
|
64
88
|
|
|
65
89
|
class BulbService(BaseService):
|
|
90
|
+
"""Bulb service for interacting with Wyze bulbs."""
|
|
91
|
+
|
|
66
92
|
async def update(self, bulb: Bulb) -> Bulb:
|
|
93
|
+
"""Update the bulb object with the latest device parameters.
|
|
94
|
+
|
|
95
|
+
:param bulb: Bulb object to update
|
|
96
|
+
:return: Updated bulb object
|
|
97
|
+
"""
|
|
67
98
|
# Get updated device_params
|
|
68
99
|
async with BaseService._update_lock:
|
|
69
100
|
bulb.device_params = await self.get_updated_params(bulb.mac)
|
|
@@ -81,10 +112,10 @@ class BulbService(BaseService):
|
|
|
81
112
|
bulb.on = value == "1"
|
|
82
113
|
elif property_id == PropertyIDs.AVAILABLE:
|
|
83
114
|
bulb.available = value == "1"
|
|
84
|
-
elif
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
115
|
+
elif property_id == PropertyIDs.COLOR and bulb.type in [
|
|
116
|
+
DeviceTypes.LIGHTSTRIP,
|
|
117
|
+
DeviceTypes.MESH_LIGHT,
|
|
118
|
+
]:
|
|
88
119
|
bulb.color = value
|
|
89
120
|
elif property_id == PropertyIDs.COLOR_MODE:
|
|
90
121
|
bulb.color_mode = value
|
|
@@ -98,23 +129,24 @@ class BulbService(BaseService):
|
|
|
98
129
|
return bulb
|
|
99
130
|
|
|
100
131
|
async def get_bulbs(self) -> List[Bulb]:
|
|
132
|
+
"""Get a list of all bulbs.
|
|
133
|
+
|
|
134
|
+
:return: List of Bulb objects
|
|
135
|
+
"""
|
|
101
136
|
if self._devices is None:
|
|
102
137
|
self._devices = await self.get_object_list()
|
|
103
138
|
|
|
104
139
|
bulbs = [
|
|
105
140
|
device
|
|
106
141
|
for device in self._devices
|
|
107
|
-
if device.type
|
|
108
|
-
|
|
109
|
-
DeviceTypes.LIGHTSTRIP]
|
|
142
|
+
if device.type
|
|
143
|
+
in [DeviceTypes.LIGHT, DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
|
|
110
144
|
]
|
|
111
145
|
|
|
112
146
|
return [Bulb(bulb.raw_dict) for bulb in bulbs]
|
|
113
147
|
|
|
114
148
|
async def turn_on(self, bulb: Bulb, local_control, options=None):
|
|
115
|
-
plist = [
|
|
116
|
-
create_pid_pair(PropertyIDs.ON, "1")
|
|
117
|
-
]
|
|
149
|
+
plist = [create_pid_pair(PropertyIDs.ON, "1")]
|
|
118
150
|
|
|
119
151
|
if options is not None:
|
|
120
152
|
plist.extend(options)
|
|
@@ -122,15 +154,15 @@ class BulbService(BaseService):
|
|
|
122
154
|
if bulb.type is DeviceTypes.LIGHT:
|
|
123
155
|
await self._set_property_list(bulb, plist)
|
|
124
156
|
|
|
125
|
-
elif
|
|
126
|
-
bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
|
|
127
|
-
):
|
|
157
|
+
elif bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]:
|
|
128
158
|
# Local Control
|
|
129
159
|
if local_control and not bulb.cloud_fallback:
|
|
130
160
|
await self._local_bulb_command(bulb, plist)
|
|
131
161
|
|
|
132
162
|
# Cloud Control
|
|
133
|
-
elif
|
|
163
|
+
elif (
|
|
164
|
+
bulb.type is DeviceTypes.MESH_LIGHT
|
|
165
|
+
): # Sun match for mesh bulbs needs to be set on a different endpoint for some reason
|
|
134
166
|
for item in plist:
|
|
135
167
|
if item["pid"] == PropertyIDs.SUN_MATCH.value:
|
|
136
168
|
await self._set_property_list(bulb, [item])
|
|
@@ -140,74 +172,46 @@ class BulbService(BaseService):
|
|
|
140
172
|
await self._run_action_list(bulb, plist) # Lightstrips
|
|
141
173
|
|
|
142
174
|
async def turn_off(self, bulb: Bulb, local_control):
|
|
143
|
-
plist = [
|
|
144
|
-
create_pid_pair(PropertyIDs.ON, "0")
|
|
145
|
-
]
|
|
175
|
+
plist = [create_pid_pair(PropertyIDs.ON, "0")]
|
|
146
176
|
|
|
147
|
-
if bulb.type in [
|
|
148
|
-
DeviceTypes.LIGHT
|
|
149
|
-
]:
|
|
177
|
+
if bulb.type in [DeviceTypes.LIGHT]:
|
|
150
178
|
await self._set_property_list(bulb, plist)
|
|
151
|
-
elif
|
|
152
|
-
bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]
|
|
153
|
-
):
|
|
179
|
+
elif bulb.type in [DeviceTypes.MESH_LIGHT, DeviceTypes.LIGHTSTRIP]:
|
|
154
180
|
if local_control and not bulb.cloud_fallback:
|
|
155
181
|
await self._local_bulb_command(bulb, plist)
|
|
156
182
|
else:
|
|
157
183
|
await self._run_action_list(bulb, plist)
|
|
158
184
|
|
|
159
185
|
async def set_color_temp(self, bulb: Bulb, color_temp: int):
|
|
160
|
-
plist = [
|
|
161
|
-
create_pid_pair(PropertyIDs.COLOR_TEMP, str(color_temp))
|
|
162
|
-
]
|
|
186
|
+
plist = [create_pid_pair(PropertyIDs.COLOR_TEMP, str(color_temp))]
|
|
163
187
|
|
|
164
|
-
if bulb.type in [
|
|
165
|
-
DeviceTypes.LIGHT
|
|
166
|
-
]:
|
|
188
|
+
if bulb.type in [DeviceTypes.LIGHT]:
|
|
167
189
|
await self._set_property_list(bulb, plist)
|
|
168
|
-
elif bulb.type in [
|
|
169
|
-
DeviceTypes.MESH_LIGHT
|
|
170
|
-
]:
|
|
190
|
+
elif bulb.type in [DeviceTypes.MESH_LIGHT]:
|
|
171
191
|
await self._local_bulb_command(bulb, plist)
|
|
172
192
|
|
|
173
193
|
async def set_color(self, bulb: Bulb, color: str, local_control):
|
|
174
|
-
plist = [
|
|
175
|
-
|
|
176
|
-
]
|
|
177
|
-
if bulb.type in [
|
|
178
|
-
DeviceTypes.MESH_LIGHT
|
|
179
|
-
]:
|
|
194
|
+
plist = [create_pid_pair(PropertyIDs.COLOR, str(color))]
|
|
195
|
+
if bulb.type in [DeviceTypes.MESH_LIGHT]:
|
|
180
196
|
if local_control and not bulb.cloud_fallback:
|
|
181
197
|
await self._local_bulb_command(bulb, plist)
|
|
182
198
|
else:
|
|
183
199
|
await self._run_action_list(bulb, plist)
|
|
184
200
|
|
|
185
201
|
async def set_brightness(self, bulb: Device, brightness: int):
|
|
186
|
-
plist = [
|
|
187
|
-
create_pid_pair(PropertyIDs.BRIGHTNESS, str(brightness))
|
|
188
|
-
]
|
|
202
|
+
plist = [create_pid_pair(PropertyIDs.BRIGHTNESS, str(brightness))]
|
|
189
203
|
|
|
190
|
-
if bulb.type in [
|
|
191
|
-
DeviceTypes.LIGHT
|
|
192
|
-
]:
|
|
204
|
+
if bulb.type in [DeviceTypes.LIGHT]:
|
|
193
205
|
await self._set_property_list(bulb, plist)
|
|
194
|
-
if bulb.type in [
|
|
195
|
-
DeviceTypes.MESH_LIGHT
|
|
196
|
-
]:
|
|
206
|
+
if bulb.type in [DeviceTypes.MESH_LIGHT]:
|
|
197
207
|
await self._local_bulb_command(bulb, plist)
|
|
198
208
|
|
|
199
209
|
async def music_mode_on(self, bulb: Device):
|
|
200
|
-
plist = [
|
|
201
|
-
create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "1")
|
|
202
|
-
]
|
|
210
|
+
plist = [create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "1")]
|
|
203
211
|
|
|
204
212
|
await self._run_action_list(bulb, plist)
|
|
205
213
|
|
|
206
214
|
async def music_mode_off(self, bulb: Device):
|
|
207
|
-
plist = [
|
|
208
|
-
create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "0")
|
|
209
|
-
]
|
|
215
|
+
plist = [create_pid_pair(PropertyIDs.LIGHTSTRIP_MUSIC_MODE, "0")]
|
|
210
216
|
|
|
211
217
|
await self._run_action_list(bulb, plist)
|
|
212
|
-
|
|
213
|
-
|
|
@@ -13,14 +13,17 @@ from aiohttp import ClientOSError, ContentTypeError
|
|
|
13
13
|
|
|
14
14
|
from ..exceptions import UnknownApiError
|
|
15
15
|
from .base_service import BaseService
|
|
16
|
-
from .update_manager import DeviceUpdater
|
|
17
16
|
from ..types import Device, DeviceTypes, Event, PropertyIDs, DeviceMgmtToggleProps
|
|
18
17
|
from ..utils import return_event_for_device, create_pid_pair
|
|
19
18
|
|
|
20
19
|
_LOGGER = logging.getLogger(__name__)
|
|
21
20
|
|
|
22
21
|
# NOTE: Make sure to also define props in devicemgmt_create_capabilities_payload()
|
|
23
|
-
DEVICEMGMT_API_MODELS = [
|
|
22
|
+
DEVICEMGMT_API_MODELS = [
|
|
23
|
+
"LD_CFP",
|
|
24
|
+
"AN_RSCW",
|
|
25
|
+
"GW_GC1",
|
|
26
|
+
] # Floodlight pro, battery cam pro, and OG use a diffrent api (devicemgmt)
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
class Camera(Device):
|
|
@@ -46,7 +49,7 @@ class CameraService(BaseService):
|
|
|
46
49
|
|
|
47
50
|
# Get camera events
|
|
48
51
|
response = await self._get_event_list(10)
|
|
49
|
-
raw_events = response[
|
|
52
|
+
raw_events = response["data"]["event_list"]
|
|
50
53
|
latest_events = [Event(raw_event) for raw_event in raw_events]
|
|
51
54
|
|
|
52
55
|
if (event := return_event_for_device(camera, latest_events)) is not None:
|
|
@@ -54,22 +57,29 @@ class CameraService(BaseService):
|
|
|
54
57
|
camera.last_event_ts = event.event_ts
|
|
55
58
|
|
|
56
59
|
# Update camera state
|
|
57
|
-
if
|
|
60
|
+
if camera.product_model in DEVICEMGMT_API_MODELS: # New api
|
|
58
61
|
state_response: Dict[str, Any] = await self._get_iot_prop_devicemgmt(camera)
|
|
59
|
-
for propCategory in state_response[
|
|
60
|
-
if propCategory[
|
|
61
|
-
camera.motion = propCategory[
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
camera.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
for propCategory in state_response["data"]["capabilities"]:
|
|
63
|
+
if propCategory["name"] == "camera":
|
|
64
|
+
camera.motion = propCategory["properties"][
|
|
65
|
+
"motion-detect-recording"
|
|
66
|
+
]
|
|
67
|
+
if (
|
|
68
|
+
propCategory["name"] == "floodlight"
|
|
69
|
+
or propCategory["name"] == "spotlight"
|
|
70
|
+
):
|
|
71
|
+
camera.floodlight = propCategory["properties"]["on"]
|
|
72
|
+
if propCategory["name"] == "siren":
|
|
73
|
+
camera.siren = propCategory["properties"]["state"]
|
|
74
|
+
if propCategory["name"] == "iot-device":
|
|
75
|
+
camera.notify = propCategory["properties"]["push-switch"]
|
|
76
|
+
camera.on = propCategory["properties"]["iot-power"]
|
|
77
|
+
camera.available = propCategory["properties"]["iot-state"]
|
|
78
|
+
|
|
79
|
+
else: # All other cam types (old api?)
|
|
80
|
+
state_response: List[
|
|
81
|
+
Tuple[PropertyIDs, Any]
|
|
82
|
+
] = await self._get_property_list(camera)
|
|
73
83
|
for property, value in state_response:
|
|
74
84
|
if property is PropertyIDs.AVAILABLE:
|
|
75
85
|
camera.available = value == "1"
|
|
@@ -80,7 +90,9 @@ class CameraService(BaseService):
|
|
|
80
90
|
if property is PropertyIDs.ACCESSORY:
|
|
81
91
|
camera.floodlight = value == "1"
|
|
82
92
|
if camera.device_params["dongle_product_model"] == "HL_CGDC":
|
|
83
|
-
camera.garage =
|
|
93
|
+
camera.garage = (
|
|
94
|
+
value == "1"
|
|
95
|
+
) # 1 = open, 2 = closed by automation or smart platform (Alexa, Google Home, Rules), 0 = closed by app
|
|
84
96
|
if property is PropertyIDs.NOTIFICATION:
|
|
85
97
|
camera.notify = value == "1"
|
|
86
98
|
if property is PropertyIDs.MOTION_DETECTION:
|
|
@@ -88,16 +100,28 @@ class CameraService(BaseService):
|
|
|
88
100
|
|
|
89
101
|
return camera
|
|
90
102
|
|
|
91
|
-
async def register_for_updates(
|
|
103
|
+
async def register_for_updates(
|
|
104
|
+
self, camera: Camera, callback: Callable[[Camera], None]
|
|
105
|
+
):
|
|
92
106
|
loop = asyncio.get_event_loop()
|
|
93
107
|
if not self._updater_thread:
|
|
94
|
-
self._updater_thread = Thread(
|
|
108
|
+
self._updater_thread = Thread(
|
|
109
|
+
target=self.update_worker,
|
|
110
|
+
args=[
|
|
111
|
+
loop,
|
|
112
|
+
],
|
|
113
|
+
daemon=True,
|
|
114
|
+
)
|
|
95
115
|
self._updater_thread.start()
|
|
96
116
|
|
|
97
117
|
self._subscribers.append((camera, callback))
|
|
98
118
|
|
|
99
119
|
async def deregister_for_updates(self, camera: Camera):
|
|
100
|
-
self._subscribers = [
|
|
120
|
+
self._subscribers = [
|
|
121
|
+
(cam, callback)
|
|
122
|
+
for cam, callback in self._subscribers
|
|
123
|
+
if cam.mac != camera.mac
|
|
124
|
+
]
|
|
101
125
|
|
|
102
126
|
def update_worker(self, loop):
|
|
103
127
|
while True:
|
|
@@ -106,9 +130,15 @@ class CameraService(BaseService):
|
|
|
106
130
|
else:
|
|
107
131
|
for camera, callback in self._subscribers:
|
|
108
132
|
try:
|
|
109
|
-
callback(
|
|
133
|
+
callback(
|
|
134
|
+
asyncio.run_coroutine_threadsafe(
|
|
135
|
+
self.update(camera), loop
|
|
136
|
+
).result()
|
|
137
|
+
)
|
|
110
138
|
except UnknownApiError as e:
|
|
111
|
-
_LOGGER.warning(
|
|
139
|
+
_LOGGER.warning(
|
|
140
|
+
f"The update method detected an UnknownApiError: {e}"
|
|
141
|
+
)
|
|
112
142
|
except ClientOSError as e:
|
|
113
143
|
_LOGGER.error(f"A network error was detected: {e}")
|
|
114
144
|
except ContentTypeError as e:
|
|
@@ -118,65 +148,121 @@ class CameraService(BaseService):
|
|
|
118
148
|
if self._devices is None:
|
|
119
149
|
self._devices = await self.get_object_list()
|
|
120
150
|
|
|
121
|
-
cameras = [
|
|
151
|
+
cameras = [
|
|
152
|
+
device for device in self._devices if device.type is DeviceTypes.CAMERA
|
|
153
|
+
]
|
|
122
154
|
|
|
123
155
|
return [Camera(camera.raw_dict) for camera in cameras]
|
|
124
156
|
|
|
125
157
|
async def turn_on(self, camera: Camera):
|
|
126
|
-
if
|
|
127
|
-
|
|
158
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
159
|
+
await self._run_action_devicemgmt(
|
|
160
|
+
camera, "power", "wakeup"
|
|
161
|
+
) # Some camera models use a diffrent api
|
|
162
|
+
else:
|
|
163
|
+
await self._run_action(camera, "power_on")
|
|
128
164
|
|
|
129
165
|
async def turn_off(self, camera: Camera):
|
|
130
|
-
if
|
|
131
|
-
|
|
166
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
167
|
+
await self._run_action_devicemgmt(
|
|
168
|
+
camera, "power", "sleep"
|
|
169
|
+
) # Some camera models use a diffrent api
|
|
170
|
+
else:
|
|
171
|
+
await self._run_action(camera, "power_off")
|
|
132
172
|
|
|
133
173
|
async def siren_on(self, camera: Camera):
|
|
134
|
-
if
|
|
135
|
-
|
|
174
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
175
|
+
await self._run_action_devicemgmt(
|
|
176
|
+
camera, "siren", "siren-on"
|
|
177
|
+
) # Some camera models use a diffrent api
|
|
178
|
+
else:
|
|
179
|
+
await self._run_action(camera, "siren_on")
|
|
136
180
|
|
|
137
181
|
async def siren_off(self, camera: Camera):
|
|
138
|
-
if
|
|
139
|
-
|
|
182
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
183
|
+
await self._run_action_devicemgmt(
|
|
184
|
+
camera, "siren", "siren-off"
|
|
185
|
+
) # Some camera models use a diffrent api
|
|
186
|
+
else:
|
|
187
|
+
await self._run_action(camera, "siren_off")
|
|
140
188
|
|
|
141
189
|
# Also controls lamp socket and BCP spotlight
|
|
142
190
|
async def floodlight_on(self, camera: Camera):
|
|
143
|
-
if
|
|
144
|
-
|
|
145
|
-
|
|
191
|
+
if camera.product_model == "AN_RSCW":
|
|
192
|
+
await self._run_action_devicemgmt(
|
|
193
|
+
camera, "spotlight", "1"
|
|
194
|
+
) # Battery cam pro integrated spotlight is controllable
|
|
195
|
+
elif camera.product_model in DEVICEMGMT_API_MODELS:
|
|
196
|
+
await self._run_action_devicemgmt(
|
|
197
|
+
camera, "floodlight", "1"
|
|
198
|
+
) # Some camera models use a diffrent api
|
|
199
|
+
else:
|
|
200
|
+
await self._set_property(camera, PropertyIDs.ACCESSORY.value, "1")
|
|
146
201
|
|
|
147
202
|
# Also controls lamp socket and BCP spotlight
|
|
148
203
|
async def floodlight_off(self, camera: Camera):
|
|
149
|
-
if
|
|
150
|
-
|
|
151
|
-
|
|
204
|
+
if camera.product_model == "AN_RSCW":
|
|
205
|
+
await self._run_action_devicemgmt(
|
|
206
|
+
camera, "spotlight", "0"
|
|
207
|
+
) # Battery cam pro integrated spotlight is controllable
|
|
208
|
+
elif camera.product_model in DEVICEMGMT_API_MODELS:
|
|
209
|
+
await self._run_action_devicemgmt(
|
|
210
|
+
camera, "floodlight", "0"
|
|
211
|
+
) # Some camera models use a diffrent api
|
|
212
|
+
else:
|
|
213
|
+
await self._set_property(camera, PropertyIDs.ACCESSORY.value, "2")
|
|
152
214
|
|
|
153
215
|
# Garage door trigger uses run action on all models
|
|
154
216
|
async def garage_door_open(self, camera: Camera):
|
|
155
217
|
await self._run_action(camera, "garage_door_trigger")
|
|
156
|
-
|
|
218
|
+
|
|
157
219
|
async def garage_door_close(self, camera: Camera):
|
|
158
220
|
await self._run_action(camera, "garage_door_trigger")
|
|
159
221
|
|
|
160
222
|
async def turn_on_notifications(self, camera: Camera):
|
|
161
|
-
if
|
|
162
|
-
|
|
223
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
224
|
+
await self._set_toggle(
|
|
225
|
+
camera, DeviceMgmtToggleProps.NOTIFICATION_TOGGLE.value, "1"
|
|
226
|
+
)
|
|
227
|
+
else:
|
|
228
|
+
await self._set_property(camera, PropertyIDs.NOTIFICATION.value, "1")
|
|
163
229
|
|
|
164
230
|
async def turn_off_notifications(self, camera: Camera):
|
|
165
|
-
if
|
|
166
|
-
|
|
231
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
232
|
+
await self._set_toggle(
|
|
233
|
+
camera, DeviceMgmtToggleProps.NOTIFICATION_TOGGLE.value, "0"
|
|
234
|
+
)
|
|
235
|
+
else:
|
|
236
|
+
await self._set_property(camera, PropertyIDs.NOTIFICATION.value, "0")
|
|
167
237
|
|
|
168
238
|
# Both properties need to be set on newer cams, older cameras seem to only react
|
|
169
239
|
# to the first property but it doesnt hurt to set both
|
|
170
240
|
async def turn_on_motion_detection(self, camera: Camera):
|
|
171
|
-
if
|
|
172
|
-
|
|
241
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
242
|
+
await self._set_toggle(
|
|
243
|
+
camera, DeviceMgmtToggleProps.EVENT_RECORDING_TOGGLE.value, "1"
|
|
244
|
+
)
|
|
245
|
+
elif camera.product_model in ["WVOD1", "HL_WCO2"]:
|
|
246
|
+
await self._set_property_list(
|
|
247
|
+
camera, [create_pid_pair(PropertyIDs.WCO_MOTION_DETECTION, "1")]
|
|
248
|
+
)
|
|
173
249
|
else:
|
|
174
250
|
await self._set_property(camera, PropertyIDs.MOTION_DETECTION.value, "1")
|
|
175
|
-
await self._set_property(
|
|
251
|
+
await self._set_property(
|
|
252
|
+
camera, PropertyIDs.MOTION_DETECTION_TOGGLE.value, "1"
|
|
253
|
+
)
|
|
176
254
|
|
|
177
255
|
async def turn_off_motion_detection(self, camera: Camera):
|
|
178
|
-
if
|
|
179
|
-
|
|
256
|
+
if camera.product_model in DEVICEMGMT_API_MODELS:
|
|
257
|
+
await self._set_toggle(
|
|
258
|
+
camera, DeviceMgmtToggleProps.EVENT_RECORDING_TOGGLE.value, "0"
|
|
259
|
+
)
|
|
260
|
+
elif camera.product_model in ["WVOD1", "HL_WCO2"]:
|
|
261
|
+
await self._set_property_list(
|
|
262
|
+
camera, [create_pid_pair(PropertyIDs.WCO_MOTION_DETECTION, "0")]
|
|
263
|
+
)
|
|
180
264
|
else:
|
|
181
265
|
await self._set_property(camera, PropertyIDs.MOTION_DETECTION.value, "0")
|
|
182
|
-
await self._set_property(
|
|
266
|
+
await self._set_property(
|
|
267
|
+
camera, PropertyIDs.MOTION_DETECTION_TOGGLE.value, "0"
|
|
268
|
+
)
|
wyzeapy/services/hms_service.py
CHANGED
|
@@ -11,16 +11,16 @@ from .base_service import BaseService
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class HMSMode(Enum):
|
|
14
|
-
CHANGING =
|
|
15
|
-
DISARMED =
|
|
16
|
-
AWAY =
|
|
17
|
-
HOME =
|
|
14
|
+
CHANGING = "changing"
|
|
15
|
+
DISARMED = "disarm"
|
|
16
|
+
AWAY = "away"
|
|
17
|
+
HOME = "home"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class HMSService(BaseService):
|
|
21
21
|
async def update(self, hms_id: str):
|
|
22
22
|
hms_mode = await self._monitoring_profile_state_status(hms_id)
|
|
23
|
-
return HMSMode(hms_mode[
|
|
23
|
+
return HMSMode(hms_mode["message"])
|
|
24
24
|
|
|
25
25
|
def __init__(self, auth_lib: WyzeAuthLib):
|
|
26
26
|
super().__init__(auth_lib)
|
|
@@ -67,22 +67,13 @@ class HMSService(BaseService):
|
|
|
67
67
|
return self._hms_id
|
|
68
68
|
|
|
69
69
|
response = await self._get_plan_binding_list_by_user()
|
|
70
|
-
hms_subs = response[
|
|
70
|
+
hms_subs = response["data"]
|
|
71
71
|
|
|
72
72
|
if len(hms_subs) >= 1:
|
|
73
73
|
for sub in hms_subs:
|
|
74
|
-
if (devices := sub.get(
|
|
74
|
+
if (devices := sub.get("deviceList")) is not None and len(devices) >= 1:
|
|
75
75
|
for device in devices:
|
|
76
|
-
self._hms_id = str(device[
|
|
76
|
+
self._hms_id = str(device["device_id"])
|
|
77
77
|
return self._hms_id
|
|
78
78
|
|
|
79
79
|
return None
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|