pyezvizapi 1.0.1.3__py3-none-any.whl → 1.0.1.4__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.

Potentially problematic release.


This version of pyezvizapi might be problematic. Click here for more details.

@@ -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 = "/v3/iot-feature/action/#SERIAL#/Video/1/DoorLockMgr/RemoteUnlockReq"
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/"
pyezvizapi/client.py CHANGED
@@ -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 len(_res_id_list) else "NONE"
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": 0,
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
- try:
1617
- endpoint = API_ENDPOINT_REMOTE_UNLOCK.replace("#SERIAL#", serial)
1618
- user_id = self._token["username"]
1619
- payload = json.dumps({
1620
- "unLockInfo": {
1621
- "bindCode": f"{FEATURE_CODE}{user_id}",
1622
- "lockNo": lock_no,
1623
- "streamToken": "",
1624
- "userName": user_id,
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']}{endpoint}",
1633
- data=payload,
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
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyezvizapi
3
- Version: 1.0.1.3
3
+ Version: 1.0.1.4
4
4
  Summary: Pilot your Ezviz cameras
5
5
  Home-page: https://github.com/RenierM26/pyEzvizApi/
6
6
  Author: Renier Moorcroft
@@ -1,19 +1,19 @@
1
1
  pyezvizapi/__init__.py,sha256=4yNAetIabR69XSbT6ZExmCbXLyW68xK7-qeDMZBzsSY,1290
2
2
  pyezvizapi/__main__.py,sha256=OsawbYa-eoLUTOMr4xWeryq4sDzs09WL9GELKQTbda8,16396
3
- pyezvizapi/api_endpoints.py,sha256=4eEj6UZw520HQyK_F6V6mpJQqFrXniIGviFJJlPJS88,2671
3
+ pyezvizapi/api_endpoints.py,sha256=rk6VinLVCn-B6DxnhfV79liplNpgUsipNbTEa_MRVwU,2755
4
4
  pyezvizapi/camera.py,sha256=BpmLySbnWWEKimSwgjx_cB60Q-6dbgdY-NA--NzUvps,11486
5
5
  pyezvizapi/cas.py,sha256=d31ZflYSD9P40MnsNRNZbT0HVLvlnHokKpLbdAjWQ74,5631
6
- pyezvizapi/client.py,sha256=-tvE5qG2NQn7inFWmTC_xSt_IOAL_3tUg38Rr3iK9Oo,80365
6
+ pyezvizapi/client.py,sha256=2veOwWHvUzoNvOO5XzIMytrIETuItBzfm_CgzUW0HIM,85150
7
7
  pyezvizapi/constants.py,sha256=jjLO-Ne9jq9m9_giYB4rnPXDZKkzKhesVXBqP1B3-00,12304
8
8
  pyezvizapi/exceptions.py,sha256=TBJGh12s4UAMVY4k3jibfjEt9D4NVgKnv-397HmfEws,608
9
9
  pyezvizapi/light_bulb.py,sha256=ADLrPZ6NL4vANzmohU63QuD9qVGkKHkX9C0o7Evbv-A,5730
10
10
  pyezvizapi/mqtt.py,sha256=Y4X99Z0Avm32SE8vog7CNsv6tGUPmPYUZUgPDGS0QJA,7866
11
11
  pyezvizapi/test_cam_rtsp.py,sha256=w7GPcYIeK78TxL8zFDihdGSDQNWcYrurwZOr6uFzzgo,4902
12
12
  pyezvizapi/utils.py,sha256=5J10o3h-y8prWDvl3LSAF-9wS1jBgBMg5cpAEebcuSM,4936
13
- pyezvizapi-1.0.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
- pyezvizapi-1.0.1.3.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
15
- pyezvizapi-1.0.1.3.dist-info/METADATA,sha256=mmUk9cGkEADYlGfqLWsB8R6YhfkIdZOX-BxX2WY4sY8,694
16
- pyezvizapi-1.0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- pyezvizapi-1.0.1.3.dist-info/entry_points.txt,sha256=_BSJ3eNb2H_AZkRdsv1s4mojqWn3N7m503ujvg1SudA,56
18
- pyezvizapi-1.0.1.3.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
19
- pyezvizapi-1.0.1.3.dist-info/RECORD,,
13
+ pyezvizapi-1.0.1.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
+ pyezvizapi-1.0.1.4.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
15
+ pyezvizapi-1.0.1.4.dist-info/METADATA,sha256=NKyWc8CwevMdhpoe3VWgsCg83vjd679ROFOXRbhIP3U,694
16
+ pyezvizapi-1.0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ pyezvizapi-1.0.1.4.dist-info/entry_points.txt,sha256=_BSJ3eNb2H_AZkRdsv1s4mojqWn3N7m503ujvg1SudA,56
18
+ pyezvizapi-1.0.1.4.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
19
+ pyezvizapi-1.0.1.4.dist-info/RECORD,,