pyezvizapi 1.0.3.3__py3-none-any.whl → 1.0.4.3__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.
- pyezvizapi/__init__.py +24 -0
- pyezvizapi/__main__.py +99 -2
- pyezvizapi/api_endpoints.py +2 -1
- pyezvizapi/camera.py +196 -15
- pyezvizapi/client.py +369 -35
- pyezvizapi/constants.py +25 -3
- pyezvizapi/feature.py +299 -14
- pyezvizapi/light_bulb.py +1 -1
- pyezvizapi/mqtt.py +22 -16
- pyezvizapi/test_cam_rtsp.py +8 -10
- pyezvizapi/test_mqtt.py +53 -11
- pyezvizapi/utils.py +146 -63
- pyezvizapi-1.0.4.3.dist-info/METADATA +286 -0
- pyezvizapi-1.0.4.3.dist-info/RECORD +21 -0
- pyezvizapi-1.0.3.3.dist-info/METADATA +0 -27
- pyezvizapi-1.0.3.3.dist-info/RECORD +0 -22
- pyezvizapi-1.0.3.3.dist-info/entry_points.txt +0 -2
- {pyezvizapi-1.0.3.3.dist-info → pyezvizapi-1.0.4.3.dist-info}/WHEEL +0 -0
- {pyezvizapi-1.0.3.3.dist-info → pyezvizapi-1.0.4.3.dist-info}/licenses/LICENSE +0 -0
- {pyezvizapi-1.0.3.3.dist-info → pyezvizapi-1.0.4.3.dist-info}/licenses/LICENSE.md +0 -0
- {pyezvizapi-1.0.3.3.dist-info → pyezvizapi-1.0.4.3.dist-info}/top_level.txt +0 -0
pyezvizapi/__init__.py
CHANGED
|
@@ -39,7 +39,11 @@ from .feature import (
|
|
|
39
39
|
day_night_sensitivity_value,
|
|
40
40
|
device_icr_dss_config,
|
|
41
41
|
display_mode_value,
|
|
42
|
+
get_algorithm_value,
|
|
43
|
+
has_algorithm_subtype,
|
|
42
44
|
has_osd_overlay,
|
|
45
|
+
iter_algorithm_entries,
|
|
46
|
+
iter_channel_algorithm_entries,
|
|
43
47
|
lens_defog_config,
|
|
44
48
|
lens_defog_value,
|
|
45
49
|
night_vision_config,
|
|
@@ -47,8 +51,16 @@ from .feature import (
|
|
|
47
51
|
night_vision_luminance_value,
|
|
48
52
|
night_vision_mode_value,
|
|
49
53
|
night_vision_payload,
|
|
54
|
+
normalize_port_security,
|
|
50
55
|
optionals_mapping,
|
|
56
|
+
port_security_config,
|
|
57
|
+
port_security_has_port,
|
|
58
|
+
port_security_port_enabled,
|
|
51
59
|
resolve_channel,
|
|
60
|
+
supplement_light_available,
|
|
61
|
+
supplement_light_enabled,
|
|
62
|
+
supplement_light_params,
|
|
63
|
+
support_ext_value,
|
|
52
64
|
)
|
|
53
65
|
from .light_bulb import EzvizLightBulb
|
|
54
66
|
from .models import EzvizDeviceRecord, build_device_records_map
|
|
@@ -91,7 +103,11 @@ __all__ = [
|
|
|
91
103
|
"day_night_sensitivity_value",
|
|
92
104
|
"device_icr_dss_config",
|
|
93
105
|
"display_mode_value",
|
|
106
|
+
"get_algorithm_value",
|
|
107
|
+
"has_algorithm_subtype",
|
|
94
108
|
"has_osd_overlay",
|
|
109
|
+
"iter_algorithm_entries",
|
|
110
|
+
"iter_channel_algorithm_entries",
|
|
95
111
|
"lens_defog_config",
|
|
96
112
|
"lens_defog_value",
|
|
97
113
|
"night_vision_config",
|
|
@@ -99,6 +115,14 @@ __all__ = [
|
|
|
99
115
|
"night_vision_luminance_value",
|
|
100
116
|
"night_vision_mode_value",
|
|
101
117
|
"night_vision_payload",
|
|
118
|
+
"normalize_port_security",
|
|
102
119
|
"optionals_mapping",
|
|
120
|
+
"port_security_config",
|
|
121
|
+
"port_security_has_port",
|
|
122
|
+
"port_security_port_enabled",
|
|
103
123
|
"resolve_channel",
|
|
124
|
+
"supplement_light_available",
|
|
125
|
+
"supplement_light_enabled",
|
|
126
|
+
"supplement_light_params",
|
|
127
|
+
"support_ext_value",
|
|
104
128
|
]
|
pyezvizapi/__main__.py
CHANGED
|
@@ -260,6 +260,37 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
|
260
260
|
"--serial", required=False, help="Optional serial to filter a single device"
|
|
261
261
|
)
|
|
262
262
|
|
|
263
|
+
parser_unified = subparsers.add_parser(
|
|
264
|
+
"unifiedmsg",
|
|
265
|
+
help="Fetch unified message list (alarm feed) and dump URLs/metadata",
|
|
266
|
+
)
|
|
267
|
+
parser_unified.add_argument(
|
|
268
|
+
"--serials",
|
|
269
|
+
required=False,
|
|
270
|
+
help="Comma-separated serials to filter (default: all devices)",
|
|
271
|
+
)
|
|
272
|
+
parser_unified.add_argument(
|
|
273
|
+
"--limit",
|
|
274
|
+
type=int,
|
|
275
|
+
default=20,
|
|
276
|
+
help="Number of messages to request (max 50; default: 20)",
|
|
277
|
+
)
|
|
278
|
+
parser_unified.add_argument(
|
|
279
|
+
"--date",
|
|
280
|
+
required=False,
|
|
281
|
+
help="Date in YYYYMMDD format (default: today in API timezone)",
|
|
282
|
+
)
|
|
283
|
+
parser_unified.add_argument(
|
|
284
|
+
"--end-time",
|
|
285
|
+
required=False,
|
|
286
|
+
help="Pagination token (msgId) returned by previous call (default: latest)",
|
|
287
|
+
)
|
|
288
|
+
parser_unified.add_argument(
|
|
289
|
+
"--urls-only",
|
|
290
|
+
action="store_true",
|
|
291
|
+
help="Print only deviceSerial + media URLs instead of full metadata",
|
|
292
|
+
)
|
|
293
|
+
|
|
263
294
|
return parser.parse_args(argv)
|
|
264
295
|
|
|
265
296
|
|
|
@@ -421,7 +452,7 @@ def _handle_devices_light(args: argparse.Namespace, client: EzvizClient) -> int:
|
|
|
421
452
|
|
|
422
453
|
def _handle_pagelist(client: EzvizClient) -> int:
|
|
423
454
|
"""Output full pagelist (raw JSON) for exploration in editors like Notepad++."""
|
|
424
|
-
data = client.
|
|
455
|
+
data = client.get_page_list()
|
|
425
456
|
_write_json(data)
|
|
426
457
|
return 0
|
|
427
458
|
|
|
@@ -437,6 +468,70 @@ def _handle_device_infos(args: argparse.Namespace, client: EzvizClient) -> int:
|
|
|
437
468
|
return 0
|
|
438
469
|
|
|
439
470
|
|
|
471
|
+
def _handle_unifiedmsg(args: argparse.Namespace, client: EzvizClient) -> int:
|
|
472
|
+
"""Fetch unified message list and optionally dump media URLs."""
|
|
473
|
+
|
|
474
|
+
response = client.get_device_messages_list(
|
|
475
|
+
serials=args.serials,
|
|
476
|
+
limit=args.limit,
|
|
477
|
+
date=args.date,
|
|
478
|
+
end_time=args.end_time or "",
|
|
479
|
+
)
|
|
480
|
+
raw_messages = response.get("message")
|
|
481
|
+
if not isinstance(raw_messages, list):
|
|
482
|
+
raw_messages = response.get("messages")
|
|
483
|
+
if not isinstance(raw_messages, list):
|
|
484
|
+
raw_messages = []
|
|
485
|
+
messages: list[dict[str, Any]] = [msg for msg in raw_messages if isinstance(msg, dict)]
|
|
486
|
+
|
|
487
|
+
def _extract_url(message: dict[str, Any]) -> str | None:
|
|
488
|
+
url = message.get("pic")
|
|
489
|
+
if not url:
|
|
490
|
+
url = message.get("defaultPic") or message.get("image")
|
|
491
|
+
if not url:
|
|
492
|
+
ext = message.get("ext")
|
|
493
|
+
if isinstance(ext, dict):
|
|
494
|
+
pics = ext.get("pics")
|
|
495
|
+
if isinstance(pics, str) and pics:
|
|
496
|
+
url = pics.split(";")[0]
|
|
497
|
+
return url
|
|
498
|
+
|
|
499
|
+
if args.urls_only:
|
|
500
|
+
for item in messages:
|
|
501
|
+
media_url = _extract_url(item)
|
|
502
|
+
if not media_url:
|
|
503
|
+
continue
|
|
504
|
+
sys.stdout.write(f"{item.get('deviceSerial', 'unknown')}: {media_url}\n")
|
|
505
|
+
return 0
|
|
506
|
+
|
|
507
|
+
if args.json:
|
|
508
|
+
_write_json(messages)
|
|
509
|
+
return 0
|
|
510
|
+
|
|
511
|
+
rows: list[dict[str, Any]] = []
|
|
512
|
+
for item in messages:
|
|
513
|
+
ext = item.get("ext")
|
|
514
|
+
ext_dict = ext if isinstance(ext, dict) else None
|
|
515
|
+
rows.append(
|
|
516
|
+
{
|
|
517
|
+
"deviceSerial": item.get("deviceSerial"),
|
|
518
|
+
"time": item.get("timeStr") or item.get("time"),
|
|
519
|
+
"subType": item.get("subType"),
|
|
520
|
+
"alarmType": ext_dict.get("alarmType") if ext_dict else None,
|
|
521
|
+
"title": item.get("title") or item.get("detail") or (ext_dict or {}).get("alarmName"),
|
|
522
|
+
"url": _extract_url(item) or "",
|
|
523
|
+
"msgId": item.get("msgId"),
|
|
524
|
+
}
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
if rows:
|
|
528
|
+
df = pd.DataFrame(rows)
|
|
529
|
+
_write_df(df)
|
|
530
|
+
else:
|
|
531
|
+
sys.stdout.write("No unified messages returned.\n")
|
|
532
|
+
return 0
|
|
533
|
+
|
|
534
|
+
|
|
440
535
|
def _handle_light(args: argparse.Namespace, client: EzvizClient) -> int:
|
|
441
536
|
"""Handle `light` subcommands (toggle/status)."""
|
|
442
537
|
light_bulb = EzvizLightBulb(client, args.serial)
|
|
@@ -599,6 +694,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
599
694
|
return _handle_pagelist(client)
|
|
600
695
|
if args.action == "device_infos":
|
|
601
696
|
return _handle_device_infos(args, client)
|
|
697
|
+
if args.action == "unifiedmsg":
|
|
698
|
+
return _handle_unifiedmsg(args, client)
|
|
602
699
|
|
|
603
700
|
except PyEzvizError as exp:
|
|
604
701
|
_LOGGER.error("%s", exp)
|
|
@@ -611,7 +708,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
611
708
|
return 2
|
|
612
709
|
finally:
|
|
613
710
|
if args.save_token and args.token_file:
|
|
614
|
-
_save_token_file(args.token_file,
|
|
711
|
+
_save_token_file(args.token_file, client.export_token())
|
|
615
712
|
client.close_session()
|
|
616
713
|
|
|
617
714
|
|
pyezvizapi/api_endpoints.py
CHANGED
|
@@ -30,7 +30,8 @@ API_ENDPOINT_ALARMINFO_GET = "/v3/alarms/v2/advanced"
|
|
|
30
30
|
API_ENDPOINT_V3_ALARMS = "/v3/alarms/"
|
|
31
31
|
API_ENDPOINT_SET_LUMINANCE = "/v3/alarms/device/alarmLight/"
|
|
32
32
|
API_ENDPOINT_ALARM_DEVICE_CHIME = "/v3/alarms/device/chime/"
|
|
33
|
-
API_ENDPOINT_REMOTE_UNLOCK = "/
|
|
33
|
+
API_ENDPOINT_REMOTE_UNLOCK = "/DoorLockMgr/RemoteUnlockReq"
|
|
34
|
+
API_ENDPOINT_REMOTE_LOCK = "/DoorLockMgr/RemoteLockReq"
|
|
34
35
|
API_ENDPOINT_AUTOUPGRADE_SWITCH = "/v3/autoupgrade/v1/switch"
|
|
35
36
|
API_ENDPOINT_UPGRADE_RULE = "/v3/upgraderule"
|
|
36
37
|
|
pyezvizapi/camera.py
CHANGED
|
@@ -22,6 +22,11 @@ if TYPE_CHECKING:
|
|
|
22
22
|
|
|
23
23
|
_LOGGER = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
+
DEFAULT_ALARM_IMAGE_URL = (
|
|
26
|
+
"https://eustatics.ezvizlife.com/ovs_mall/web/img/index/EZVIZ_logo.png?ver=3007907502"
|
|
27
|
+
)
|
|
28
|
+
UNIFIEDMSG_LOOKBACK_DAYS = 7
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
class CameraStatus(TypedDict, total=False):
|
|
27
32
|
"""Typed mapping for Ezviz camera status payload."""
|
|
@@ -133,25 +138,54 @@ class EzvizCamera:
|
|
|
133
138
|
"""Fetch dictionary key."""
|
|
134
139
|
return fetch_nested_value(self._device, keys, default_value)
|
|
135
140
|
|
|
136
|
-
def _alarm_list(self) -> None:
|
|
137
|
-
"""
|
|
141
|
+
def _alarm_list(self, prefetched: dict[str, Any] | None = None) -> None:
|
|
142
|
+
"""Populate last alarm info for this camera.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
prefetched: Optional unified message payload provided by the caller to
|
|
146
|
+
avoid an extra API request. When ``None``, the camera will query the
|
|
147
|
+
cloud API directly with a short date lookback window.
|
|
138
148
|
|
|
139
149
|
Raises:
|
|
140
150
|
InvalidURL: If the API endpoint/connection is invalid.
|
|
141
151
|
HTTPError: If the API returns a non-success HTTP status.
|
|
142
152
|
PyEzvizError: On Ezviz API contract errors or decoding failures.
|
|
143
153
|
"""
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
total = fetch_nested_value(_alarmlist, ["page", "totalResults"], 0)
|
|
147
|
-
if total and total > 0:
|
|
148
|
-
self._last_alarm = _alarmlist.get("alarms", [{}])[0]
|
|
154
|
+
if prefetched:
|
|
155
|
+
self._last_alarm = self._normalize_unified_message(prefetched)
|
|
149
156
|
_LOGGER.debug(
|
|
150
|
-
"
|
|
157
|
+
"Using prefetched alarm for %s: %s", self._serial, self._last_alarm
|
|
151
158
|
)
|
|
152
159
|
self._motion_trigger()
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
response = self._client.get_device_messages_list(
|
|
163
|
+
serials=self._serial,
|
|
164
|
+
limit=1,
|
|
165
|
+
date="",
|
|
166
|
+
end_time="",
|
|
167
|
+
)
|
|
168
|
+
messages = response.get("message") or response.get("messages") or []
|
|
169
|
+
if not isinstance(messages, list):
|
|
170
|
+
messages = []
|
|
171
|
+
latest_message = next(
|
|
172
|
+
(
|
|
173
|
+
msg
|
|
174
|
+
for msg in messages
|
|
175
|
+
if isinstance(msg, dict)
|
|
176
|
+
and msg.get("deviceSerial") == self._serial
|
|
177
|
+
),
|
|
178
|
+
None,
|
|
179
|
+
)
|
|
180
|
+
if latest_message is None:
|
|
181
|
+
_LOGGER.debug(
|
|
182
|
+
"No unified messages found for %s today",
|
|
183
|
+
self._serial,
|
|
184
|
+
)
|
|
185
|
+
return
|
|
186
|
+
self._last_alarm = self._normalize_unified_message(latest_message)
|
|
187
|
+
_LOGGER.debug("Fetched last alarm for %s: %s", self._serial, self._last_alarm)
|
|
188
|
+
self._motion_trigger()
|
|
155
189
|
|
|
156
190
|
def _local_ip(self) -> str:
|
|
157
191
|
"""Fix empty ip value for certain cameras."""
|
|
@@ -170,6 +204,36 @@ class EzvizCamera:
|
|
|
170
204
|
|
|
171
205
|
return "0.0.0.0"
|
|
172
206
|
|
|
207
|
+
def _resource_route(
|
|
208
|
+
self,
|
|
209
|
+
) -> tuple[str, str, str | None, str | None]:
|
|
210
|
+
"""Return resource id, local index, stream token, and optional type."""
|
|
211
|
+
resource_infos = self._device.get("resourceInfos") or []
|
|
212
|
+
info: dict[str, Any] | None = None
|
|
213
|
+
if isinstance(resource_infos, list):
|
|
214
|
+
info = next((item for item in resource_infos if isinstance(item, dict)), None)
|
|
215
|
+
elif isinstance(resource_infos, dict):
|
|
216
|
+
info = next(
|
|
217
|
+
(item for item in resource_infos.values() if isinstance(item, dict)), None
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
resource_id = "Video"
|
|
221
|
+
local_index: str = "1"
|
|
222
|
+
stream_token: str | None = None
|
|
223
|
+
lock_type: str | None = None
|
|
224
|
+
|
|
225
|
+
if info:
|
|
226
|
+
if isinstance(info.get("resourceId"), str):
|
|
227
|
+
resource_id = info["resourceId"]
|
|
228
|
+
local_idx_value = info.get("localIndex")
|
|
229
|
+
if isinstance(local_idx_value, (int, str)):
|
|
230
|
+
local_index = str(local_idx_value)
|
|
231
|
+
stream_token = info.get("streamToken")
|
|
232
|
+
if isinstance(info.get("type"), str):
|
|
233
|
+
lock_type = info["type"]
|
|
234
|
+
|
|
235
|
+
return resource_id, local_index, stream_token, lock_type
|
|
236
|
+
|
|
173
237
|
def _motion_trigger(self) -> None:
|
|
174
238
|
"""Create motion sensor based on last alarm time.
|
|
175
239
|
|
|
@@ -186,6 +250,68 @@ class EzvizCamera:
|
|
|
186
250
|
"last_alarm_time_str": last_alarm_str,
|
|
187
251
|
}
|
|
188
252
|
|
|
253
|
+
def _normalize_unified_message(self, message: dict[str, Any]) -> dict[str, Any]:
|
|
254
|
+
"""Normalize unified message payload to legacy alarm shape."""
|
|
255
|
+
ext = message.get("ext")
|
|
256
|
+
if not isinstance(ext, dict):
|
|
257
|
+
ext = {}
|
|
258
|
+
|
|
259
|
+
pics_field = ext.get("pics")
|
|
260
|
+
multi_pic = None
|
|
261
|
+
if isinstance(pics_field, str) and pics_field:
|
|
262
|
+
multi_pic = next(
|
|
263
|
+
(part for part in pics_field.split(";") if part), None
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def _first_valid(*candidates: Any) -> str:
|
|
267
|
+
for candidate in candidates:
|
|
268
|
+
if isinstance(candidate, str) and candidate:
|
|
269
|
+
return candidate
|
|
270
|
+
return DEFAULT_ALARM_IMAGE_URL
|
|
271
|
+
|
|
272
|
+
pic_url = _first_valid(
|
|
273
|
+
message.get("pic"),
|
|
274
|
+
multi_pic,
|
|
275
|
+
message.get("defaultPic"),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
alarm_name = (
|
|
279
|
+
message.get("title")
|
|
280
|
+
or message.get("detail")
|
|
281
|
+
or message.get("sampleName")
|
|
282
|
+
or "NoAlarm"
|
|
283
|
+
)
|
|
284
|
+
alarm_type = ext.get("alarmType") or message.get("subType") or "0000"
|
|
285
|
+
|
|
286
|
+
time_value: Any = message.get("time")
|
|
287
|
+
if isinstance(time_value, str):
|
|
288
|
+
try:
|
|
289
|
+
time_value = int(time_value)
|
|
290
|
+
except (TypeError, ValueError):
|
|
291
|
+
try:
|
|
292
|
+
time_value = float(time_value)
|
|
293
|
+
except (TypeError, ValueError):
|
|
294
|
+
time_value = None
|
|
295
|
+
|
|
296
|
+
time_str = message.get("timeStr") or ext.get("alarmStartTime")
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
"alarmId": message.get("msgId"),
|
|
300
|
+
"deviceSerial": message.get("deviceSerial"),
|
|
301
|
+
"channel": message.get("channel"),
|
|
302
|
+
"alarmStartTime": time_value,
|
|
303
|
+
"alarmStartTimeStr": time_str,
|
|
304
|
+
"alarmTime": time_value,
|
|
305
|
+
"alarmTimeStr": time_str,
|
|
306
|
+
"picUrl": pic_url,
|
|
307
|
+
"picChecksum": message.get("picChecksum") or ext.get("picChecksum"),
|
|
308
|
+
"picCrypt": message.get("picCrypt") or ext.get("picCrypt"),
|
|
309
|
+
"sampleName": alarm_name,
|
|
310
|
+
"alarmType": alarm_type,
|
|
311
|
+
"msgSource": "unifiedmsg",
|
|
312
|
+
"ext": ext,
|
|
313
|
+
}
|
|
314
|
+
|
|
189
315
|
def _get_tzinfo(self) -> datetime.tzinfo:
|
|
190
316
|
"""Return tzinfo from camera setting if recognizable, else local tzinfo."""
|
|
191
317
|
tz_val = self.fetch_key(["STATUS", "optionals", "timeZone"])
|
|
@@ -204,10 +330,17 @@ class EzvizCamera:
|
|
|
204
330
|
)
|
|
205
331
|
return bool(sched and sched.get("enable"))
|
|
206
332
|
|
|
207
|
-
def status(
|
|
333
|
+
def status(
|
|
334
|
+
self,
|
|
335
|
+
refresh: bool = True,
|
|
336
|
+
*,
|
|
337
|
+
latest_alarm: dict[str, Any] | None = None,
|
|
338
|
+
) -> CameraStatus:
|
|
208
339
|
"""Return the status of the camera.
|
|
209
340
|
|
|
210
341
|
refresh: if True, updates alarm info via network before composing status.
|
|
342
|
+
latest_alarm: Optional prefetched unified message payload to avoid an extra
|
|
343
|
+
HTTP request when ``refresh`` is True.
|
|
211
344
|
|
|
212
345
|
Raises:
|
|
213
346
|
InvalidURL: If the API endpoint/connection is invalid while refreshing.
|
|
@@ -215,7 +348,7 @@ class EzvizCamera:
|
|
|
215
348
|
PyEzvizError: On Ezviz API contract errors or decoding failures.
|
|
216
349
|
"""
|
|
217
350
|
if refresh:
|
|
218
|
-
self._alarm_list()
|
|
351
|
+
self._alarm_list(prefetched=latest_alarm)
|
|
219
352
|
|
|
220
353
|
name = (
|
|
221
354
|
self._record.name
|
|
@@ -301,7 +434,7 @@ class EzvizCamera:
|
|
|
301
434
|
or self._last_alarm.get("alarmStartTimeStr"),
|
|
302
435
|
"last_alarm_pic": self._last_alarm.get(
|
|
303
436
|
"picUrl",
|
|
304
|
-
|
|
437
|
+
DEFAULT_ALARM_IMAGE_URL,
|
|
305
438
|
),
|
|
306
439
|
"last_alarm_type_code": self._last_alarm.get("alarmType", "0000"),
|
|
307
440
|
"last_alarm_type_name": self._last_alarm.get("sampleName", "NoAlarm"),
|
|
@@ -397,7 +530,16 @@ class EzvizCamera:
|
|
|
397
530
|
"""
|
|
398
531
|
_LOGGER.debug("Remote door unlock for %s", self._serial)
|
|
399
532
|
user = str(getattr(self._client, "_token", {}).get("username", ""))
|
|
400
|
-
|
|
533
|
+
resource_id, local_index, stream_token, lock_type = self._resource_route()
|
|
534
|
+
return self._client.remote_unlock(
|
|
535
|
+
self._serial,
|
|
536
|
+
user,
|
|
537
|
+
2,
|
|
538
|
+
resource_id=resource_id,
|
|
539
|
+
local_index=local_index,
|
|
540
|
+
stream_token=stream_token,
|
|
541
|
+
lock_type=lock_type,
|
|
542
|
+
)
|
|
401
543
|
|
|
402
544
|
def gate_unlock(self) -> bool:
|
|
403
545
|
"""Unlock the gate lock.
|
|
@@ -409,7 +551,46 @@ class EzvizCamera:
|
|
|
409
551
|
"""
|
|
410
552
|
_LOGGER.debug("Remote gate unlock for %s", self._serial)
|
|
411
553
|
user = str(getattr(self._client, "_token", {}).get("username", ""))
|
|
412
|
-
|
|
554
|
+
resource_id, local_index, stream_token, lock_type = self._resource_route()
|
|
555
|
+
return self._client.remote_unlock(
|
|
556
|
+
self._serial,
|
|
557
|
+
user,
|
|
558
|
+
1,
|
|
559
|
+
resource_id=resource_id,
|
|
560
|
+
local_index=local_index,
|
|
561
|
+
stream_token=stream_token,
|
|
562
|
+
lock_type=lock_type,
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
def door_lock(self) -> bool:
|
|
566
|
+
"""Lock the door remotely."""
|
|
567
|
+
_LOGGER.debug("Remote door lock for %s", self._serial)
|
|
568
|
+
user = str(getattr(self._client, "_token", {}).get("username", ""))
|
|
569
|
+
resource_id, local_index, stream_token, lock_type = self._resource_route()
|
|
570
|
+
return self._client.remote_lock(
|
|
571
|
+
self._serial,
|
|
572
|
+
user,
|
|
573
|
+
2,
|
|
574
|
+
resource_id=resource_id,
|
|
575
|
+
local_index=local_index,
|
|
576
|
+
stream_token=stream_token,
|
|
577
|
+
lock_type=lock_type,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
def gate_lock(self) -> bool:
|
|
581
|
+
"""Lock the gate remotely."""
|
|
582
|
+
_LOGGER.debug("Remote gate lock for %s", self._serial)
|
|
583
|
+
user = str(getattr(self._client, "_token", {}).get("username", ""))
|
|
584
|
+
resource_id, local_index, stream_token, lock_type = self._resource_route()
|
|
585
|
+
return self._client.remote_lock(
|
|
586
|
+
self._serial,
|
|
587
|
+
user,
|
|
588
|
+
1,
|
|
589
|
+
resource_id=resource_id,
|
|
590
|
+
local_index=local_index,
|
|
591
|
+
stream_token=stream_token,
|
|
592
|
+
lock_type=lock_type,
|
|
593
|
+
)
|
|
413
594
|
|
|
414
595
|
def alarm_notify(self, enable: bool) -> bool:
|
|
415
596
|
"""Enable/Disable camera notification when movement is detected.
|