pyezvizapi 1.0.1.2__tar.gz → 1.0.1.4__tar.gz
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.
Potentially problematic release.
This version of pyezvizapi might be problematic. Click here for more details.
- {pyezvizapi-1.0.1.2/pyezvizapi.egg-info → pyezvizapi-1.0.1.4}/PKG-INFO +1 -1
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/__init__.py +2 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/api_endpoints.py +3 -1
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/camera.py +3 -3
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/client.py +148 -20
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/constants.py +14 -3
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4/pyezvizapi.egg-info}/PKG-INFO +1 -1
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/setup.py +1 -1
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/LICENSE +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/LICENSE.md +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/MANIFEST.in +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/README.md +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/__main__.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/cas.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/exceptions.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/light_bulb.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/mqtt.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/test_cam_rtsp.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi/utils.py +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi.egg-info/SOURCES.txt +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi.egg-info/dependency_links.txt +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi.egg-info/entry_points.txt +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi.egg-info/requires.txt +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/pyezvizapi.egg-info/top_level.txt +0 -0
- {pyezvizapi-1.0.1.2 → pyezvizapi-1.0.1.4}/setup.cfg +0 -0
|
@@ -5,6 +5,7 @@ from .cas import EzvizCAS
|
|
|
5
5
|
from .client import EzvizClient
|
|
6
6
|
from .constants import (
|
|
7
7
|
AlarmDetectHumanCar,
|
|
8
|
+
BatteryCameraNewWorkMode,
|
|
8
9
|
BatteryCameraWorkMode,
|
|
9
10
|
DefenseModeType,
|
|
10
11
|
DeviceCatagories,
|
|
@@ -32,6 +33,7 @@ from .test_cam_rtsp import TestRTSPAuth
|
|
|
32
33
|
__all__ = [
|
|
33
34
|
"AlarmDetectHumanCar",
|
|
34
35
|
"AuthTestResultFailed",
|
|
36
|
+
"BatteryCameraNewWorkMode",
|
|
35
37
|
"BatteryCameraWorkMode",
|
|
36
38
|
"DefenseModeType",
|
|
37
39
|
"DeviceCatagories",
|
|
@@ -17,14 +17,16 @@ API_ENDPOINT_OSD = "/v3/userdevices/v1/devices/"
|
|
|
17
17
|
API_ENDPOINT_PANORAMIC_DEVICES_OPERATION = "/v3/panoramicDevices/operation"
|
|
18
18
|
API_ENDPOINT_UPGRADE_DEVICE = "/v3/upgrades/v1/devices/"
|
|
19
19
|
API_ENDPOINT_SEND_CODE = "/v3/sms/nologin/checkcode"
|
|
20
|
+
API_ENDPOINT_2FA_VALIDATE_POST_AUTH = "/v3/users/checkcode/mt"
|
|
20
21
|
API_ENDPOINT_UNIFIEDMSG_LIST_GET = "/v3/unifiedmsg/list"
|
|
21
22
|
API_ENDPOINT_IOT_FEATURE = "/v3/iot-feature/feature/"
|
|
23
|
+
API_ENDPOINT_IOT_ACTION = "/v3/iot-feature/action/"
|
|
22
24
|
API_ENDPOINT_CALLING_NOTIFY = "/v3/calling/"
|
|
23
25
|
|
|
24
26
|
API_ENDPOINT_ALARMINFO_GET = "/v3/alarms/v2/advanced"
|
|
25
27
|
API_ENDPOINT_V3_ALARMS = "/v3/alarms/"
|
|
26
28
|
API_ENDPOINT_SET_LUMINANCE = "/v3/alarms/device/alarmLight/"
|
|
27
|
-
API_ENDPOINT_REMOTE_UNLOCK = "/
|
|
29
|
+
API_ENDPOINT_REMOTE_UNLOCK = "/Video/1/DoorLockMgr/RemoteUnlockReq"
|
|
28
30
|
|
|
29
31
|
API_ENDPOINT_DEVCONFIG_BY_KEY = "/v3/devconfig/v1/keyValue/"
|
|
30
32
|
API_ENDPOINT_CAM_AUTH_CODE = "/v3/devconfig/authcode/query/"
|
|
@@ -158,9 +158,9 @@ class EzvizCamera:
|
|
|
158
158
|
"NightVision_Model": self.fetch_key(
|
|
159
159
|
["STATUS", "optionals", "NightVision_Model"]
|
|
160
160
|
),
|
|
161
|
-
"battery_camera_work_mode":
|
|
162
|
-
|
|
163
|
-
)
|
|
161
|
+
"battery_camera_work_mode": self.fetch_key(
|
|
162
|
+
["STATUS", "optionals", "batteryCameraWorkMode"], -1
|
|
163
|
+
),
|
|
164
164
|
"Alarm_AdvancedDetect": self.fetch_key(
|
|
165
165
|
["STATUS", "optionals", "Alarm_AdvancedDetect", "type"]
|
|
166
166
|
),
|
|
@@ -13,6 +13,7 @@ from uuid import uuid4
|
|
|
13
13
|
import requests
|
|
14
14
|
|
|
15
15
|
from .api_endpoints import (
|
|
16
|
+
API_ENDPOINT_2FA_VALIDATE_POST_AUTH,
|
|
16
17
|
API_ENDPOINT_ALARM_SOUND,
|
|
17
18
|
API_ENDPOINT_ALARMINFO_GET,
|
|
18
19
|
API_ENDPOINT_CALLING_NOTIFY,
|
|
@@ -31,6 +32,7 @@ from .api_endpoints import (
|
|
|
31
32
|
API_ENDPOINT_DO_NOT_DISTURB,
|
|
32
33
|
API_ENDPOINT_GROUP_DEFENCE_MODE,
|
|
33
34
|
API_ENDPOINT_INTELLIGENT_APP,
|
|
35
|
+
API_ENDPOINT_IOT_ACTION,
|
|
34
36
|
API_ENDPOINT_IOT_FEATURE,
|
|
35
37
|
API_ENDPOINT_LOGIN,
|
|
36
38
|
API_ENDPOINT_LOGOUT,
|
|
@@ -38,9 +40,9 @@ from .api_endpoints import (
|
|
|
38
40
|
API_ENDPOINT_OSD,
|
|
39
41
|
API_ENDPOINT_PAGELIST,
|
|
40
42
|
API_ENDPOINT_PANORAMIC_DEVICES_OPERATION,
|
|
41
|
-
API_ENDPOINT_REMOTE_UNLOCK,
|
|
42
43
|
API_ENDPOINT_PTZCONTROL,
|
|
43
44
|
API_ENDPOINT_REFRESH_SESSION_ID,
|
|
45
|
+
API_ENDPOINT_REMOTE_UNLOCK,
|
|
44
46
|
API_ENDPOINT_RETURN_PANORAMIC,
|
|
45
47
|
API_ENDPOINT_SEND_CODE,
|
|
46
48
|
API_ENDPOINT_SERVER_INFO,
|
|
@@ -1271,7 +1273,7 @@ class EzvizClient:
|
|
|
1271
1273
|
for item in devices.get("CLOUD", {})
|
|
1272
1274
|
if devices["CLOUD"][item].get("deviceSerial") == _serial
|
|
1273
1275
|
}
|
|
1274
|
-
_res_id = _res_id_list.pop() if
|
|
1276
|
+
_res_id = _res_id_list.pop() if _res_id_list else "NONE"
|
|
1275
1277
|
|
|
1276
1278
|
result[_serial] = {
|
|
1277
1279
|
"CLOUD": {_res_id: devices.get("CLOUD", {}).get(_res_id, {})},
|
|
@@ -1357,7 +1359,25 @@ class EzvizClient:
|
|
|
1357
1359
|
def get_cam_key(
|
|
1358
1360
|
self, serial: str, smscode: int | None = None, max_retries: int = 0
|
|
1359
1361
|
) -> Any:
|
|
1360
|
-
"""Get Camera encryption key. The key that is set after the camera is added to the account.
|
|
1362
|
+
"""Get Camera encryption key. The key that is set after the camera is added to the account.
|
|
1363
|
+
|
|
1364
|
+
Args:
|
|
1365
|
+
serial (str): The camera serial number.
|
|
1366
|
+
smscode (int | None): The 2FA code account when rights elevation is required.
|
|
1367
|
+
max_retries (int): The maximum number of retries. Defaults to 0.
|
|
1368
|
+
|
|
1369
|
+
Raises:
|
|
1370
|
+
PyEzvizError: If the camera encryption key can't be retrieved.
|
|
1371
|
+
EzvizAuthVerificationCode: If the account requires elevation with 2FA code.
|
|
1372
|
+
|
|
1373
|
+
Returns:
|
|
1374
|
+
Any: JSON response, filtered to return encryptkey:
|
|
1375
|
+
{
|
|
1376
|
+
"resultCode": int, # Result code (0 if successful)
|
|
1377
|
+
"encryptkey": str, # Camera encryption key
|
|
1378
|
+
"resultDes": str # Status message in chinese
|
|
1379
|
+
}
|
|
1380
|
+
"""
|
|
1361
1381
|
|
|
1362
1382
|
if max_retries > MAX_RETRIES:
|
|
1363
1383
|
raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
|
|
@@ -1421,9 +1441,33 @@ class EzvizClient:
|
|
|
1421
1441
|
serial: str,
|
|
1422
1442
|
encrypt_pwd: str | None = None,
|
|
1423
1443
|
msg_auth_code: int | None = None,
|
|
1444
|
+
sender_type: int = 0,
|
|
1424
1445
|
max_retries: int = 0,
|
|
1425
1446
|
) -> Any:
|
|
1426
|
-
"""Get Camera auth code. This is the verification code on the camera sticker.
|
|
1447
|
+
"""Get Camera auth code. This is the verification code on the camera sticker.
|
|
1448
|
+
|
|
1449
|
+
Args:
|
|
1450
|
+
serial (str): The camera serial number.
|
|
1451
|
+
encrypt_pwd (str | None): This is always none.
|
|
1452
|
+
msg_auth_code (int | None): The 2FA code.
|
|
1453
|
+
sender_type (int): The sender type. Defaults to 0. Needs to be 3 when returning 2FA code.
|
|
1454
|
+
max_retries (int): The maximum number of retries. Defaults to 0.
|
|
1455
|
+
|
|
1456
|
+
Raises:
|
|
1457
|
+
PyEzvizError: If the camera auth code cannot be retrieved.
|
|
1458
|
+
EzvizAuthVerificationCode: If the operation requires elevation with 2FA.
|
|
1459
|
+
|
|
1460
|
+
Returns:
|
|
1461
|
+
Any: JSON response, filtered to return devAuthCode:
|
|
1462
|
+
{
|
|
1463
|
+
"devAuthCode": str, # Device authorization code
|
|
1464
|
+
"meta": {
|
|
1465
|
+
"code": int, # Status code (200 if successful)
|
|
1466
|
+
"message": str, # Status message in chinese
|
|
1467
|
+
"moreInfo": null or {"INVALID_PARAMETER": str}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
"""
|
|
1427
1471
|
|
|
1428
1472
|
if max_retries > MAX_RETRIES:
|
|
1429
1473
|
raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
|
|
@@ -1431,7 +1475,7 @@ class EzvizClient:
|
|
|
1431
1475
|
params: dict[str, int | str | None] = {
|
|
1432
1476
|
"encrptPwd": encrypt_pwd,
|
|
1433
1477
|
"msgAuthCode": msg_auth_code,
|
|
1434
|
-
"senderType":
|
|
1478
|
+
"senderType": sender_type,
|
|
1435
1479
|
}
|
|
1436
1480
|
|
|
1437
1481
|
try:
|
|
@@ -1464,6 +1508,9 @@ class EzvizClient:
|
|
|
1464
1508
|
+ str(req.text)
|
|
1465
1509
|
) from err
|
|
1466
1510
|
|
|
1511
|
+
if json_output["meta"]["code"] == 80000:
|
|
1512
|
+
raise EzvizAuthVerificationCode("Operation requires 2FA check")
|
|
1513
|
+
|
|
1467
1514
|
if json_output["meta"]["code"] != 200:
|
|
1468
1515
|
raise PyEzvizError(
|
|
1469
1516
|
f"Could not get camera verification key: Got {json_output})"
|
|
@@ -1471,6 +1518,75 @@ class EzvizClient:
|
|
|
1471
1518
|
|
|
1472
1519
|
return json_output["devAuthCode"]
|
|
1473
1520
|
|
|
1521
|
+
def get_2fa_check_code(
|
|
1522
|
+
self,
|
|
1523
|
+
biz_type: str = "DEVICE_AUTH_CODE",
|
|
1524
|
+
username: str | None = None,
|
|
1525
|
+
max_retries: int = 0,
|
|
1526
|
+
) -> Any:
|
|
1527
|
+
"""Initiate 2FA check for sensitive operations. Elevates your session token permission.
|
|
1528
|
+
|
|
1529
|
+
Args:
|
|
1530
|
+
biz_type (str): The operation type. (DEVICE_ENCRYPTION | DEVICE_AUTH_CODE)
|
|
1531
|
+
username (str): The account username.
|
|
1532
|
+
max_retries (int): The maximum number of retries. Defaults to 0.
|
|
1533
|
+
|
|
1534
|
+
Raises:
|
|
1535
|
+
PyEzvizError: If the operation fails.
|
|
1536
|
+
|
|
1537
|
+
Returns:
|
|
1538
|
+
Any: JSON response with the following structure:
|
|
1539
|
+
{
|
|
1540
|
+
"meta": {
|
|
1541
|
+
"code": int, # Status code (200 if successful)
|
|
1542
|
+
"message": str # Status message in chinese
|
|
1543
|
+
"moreInfo": null
|
|
1544
|
+
},
|
|
1545
|
+
"contact": {
|
|
1546
|
+
"type": str, # 2FA code will be sent to this (EMAIL)
|
|
1547
|
+
"fuzzyContact": str # Destination value (e.g., someone@email.local)
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
"""
|
|
1551
|
+
|
|
1552
|
+
if max_retries > MAX_RETRIES:
|
|
1553
|
+
raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
|
|
1554
|
+
|
|
1555
|
+
try:
|
|
1556
|
+
req = self._session.post(
|
|
1557
|
+
url=f"https://{self._token['api_url']}{API_ENDPOINT_2FA_VALIDATE_POST_AUTH}",
|
|
1558
|
+
data={"bizType": biz_type, "from": username},
|
|
1559
|
+
timeout=self._timeout,
|
|
1560
|
+
)
|
|
1561
|
+
|
|
1562
|
+
req.raise_for_status()
|
|
1563
|
+
|
|
1564
|
+
except requests.HTTPError as err:
|
|
1565
|
+
if err.response.status_code == 401:
|
|
1566
|
+
# session is wrong, need to relogin
|
|
1567
|
+
self.login()
|
|
1568
|
+
return self.get_2fa_check_code(biz_type, username, max_retries + 1)
|
|
1569
|
+
|
|
1570
|
+
raise HTTPError from err
|
|
1571
|
+
|
|
1572
|
+
try:
|
|
1573
|
+
json_output = req.json()
|
|
1574
|
+
|
|
1575
|
+
except ValueError as err:
|
|
1576
|
+
raise PyEzvizError(
|
|
1577
|
+
"Impossible to decode response: "
|
|
1578
|
+
+ str(err)
|
|
1579
|
+
+ "\nResponse was: "
|
|
1580
|
+
+ str(req.text)
|
|
1581
|
+
) from err
|
|
1582
|
+
|
|
1583
|
+
if json_output["meta"]["code"] != 200:
|
|
1584
|
+
raise PyEzvizError(
|
|
1585
|
+
f"Could not request elevated permission: Got {json_output})"
|
|
1586
|
+
)
|
|
1587
|
+
|
|
1588
|
+
return json_output
|
|
1589
|
+
|
|
1474
1590
|
def create_panoramic(self, serial: str, max_retries: int = 0) -> Any:
|
|
1475
1591
|
"""Create panoramic image."""
|
|
1476
1592
|
|
|
@@ -1611,26 +1727,38 @@ class EzvizClient:
|
|
|
1611
1727
|
|
|
1612
1728
|
return True
|
|
1613
1729
|
|
|
1614
|
-
def remote_unlock(self, serial: str, lock_no: int) -> bool:
|
|
1615
|
-
"""Sends a remote command to unlock a specific lock.
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1730
|
+
def remote_unlock(self, serial: str, user_id: str, lock_no: int) -> bool:
|
|
1731
|
+
"""Sends a remote command to unlock a specific lock.
|
|
1732
|
+
|
|
1733
|
+
Args:
|
|
1734
|
+
serial (str): The camera serial.
|
|
1735
|
+
user_id (str): The user id.
|
|
1736
|
+
lock_no (int): The lock number.
|
|
1737
|
+
|
|
1738
|
+
Raises:
|
|
1739
|
+
PyEzvizError: If max retries are exceeded or if the response indicates failure.
|
|
1740
|
+
HTTPError: If an HTTP error occurs (other than a 401, which triggers re-login).
|
|
1741
|
+
|
|
1742
|
+
Returns:
|
|
1743
|
+
bool: True if the operation was successful.
|
|
1627
1744
|
|
|
1745
|
+
"""
|
|
1746
|
+
try:
|
|
1628
1747
|
headers = self._session.headers
|
|
1629
1748
|
headers.update({"Content-Type": "application/json"})
|
|
1630
1749
|
|
|
1631
1750
|
req = self._session.put(
|
|
1632
|
-
url=f"https://{self._token['api_url']}{
|
|
1633
|
-
data=
|
|
1751
|
+
url=f"https://{self._token['api_url']}{API_ENDPOINT_IOT_ACTION}{serial}{API_ENDPOINT_REMOTE_UNLOCK}",
|
|
1752
|
+
data=json.dumps(
|
|
1753
|
+
{
|
|
1754
|
+
"unLockInfo": {
|
|
1755
|
+
"bindCode": f"{FEATURE_CODE}{user_id}",
|
|
1756
|
+
"lockNo": lock_no,
|
|
1757
|
+
"streamToken": "",
|
|
1758
|
+
"userName": user_id,
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
),
|
|
1634
1762
|
timeout=self._timeout,
|
|
1635
1763
|
headers=headers,
|
|
1636
1764
|
)
|
|
@@ -426,14 +426,25 @@ class DisplayMode(Enum):
|
|
|
426
426
|
class BatteryCameraWorkMode(Enum):
|
|
427
427
|
"""Battery camera work modes."""
|
|
428
428
|
|
|
429
|
-
|
|
430
|
-
HIGH_PERFORMANCE = 1
|
|
429
|
+
UNKNOWN = -1
|
|
431
430
|
POWER_SAVE = 0
|
|
431
|
+
HIGH_PERFORMANCE = 1
|
|
432
|
+
PLUGGED_IN = 2
|
|
432
433
|
SUPER_POWER_SAVE = 3
|
|
433
434
|
CUSTOM = 4
|
|
434
435
|
HYBERNATE = 5 # not sure
|
|
435
|
-
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@unique
|
|
439
|
+
class BatteryCameraNewWorkMode(Enum):
|
|
440
|
+
"""New battery camera work modes."""
|
|
441
|
+
|
|
436
442
|
UNKNOWN = -1
|
|
443
|
+
STANDARD = 1
|
|
444
|
+
PLUGGED_IN = 2
|
|
445
|
+
SUPER_POWER_SAVE = 3
|
|
446
|
+
CUSTOM = 4
|
|
447
|
+
ALWAYS_ON_VIDEO = 7
|
|
437
448
|
|
|
438
449
|
|
|
439
450
|
class DeviceCatagories(Enum):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|