pyezvizapi 1.0.1.3__tar.gz → 1.0.1.5__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.

Files changed (25) hide show
  1. {pyezvizapi-1.0.1.3/pyezvizapi.egg-info → pyezvizapi-1.0.1.5}/PKG-INFO +1 -1
  2. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/__init__.py +2 -0
  3. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/api_endpoints.py +3 -1
  4. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/client.py +158 -21
  5. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/exceptions.py +4 -0
  6. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5/pyezvizapi.egg-info}/PKG-INFO +1 -1
  7. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/setup.py +1 -1
  8. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/LICENSE +0 -0
  9. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/LICENSE.md +0 -0
  10. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/MANIFEST.in +0 -0
  11. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/README.md +0 -0
  12. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/__main__.py +0 -0
  13. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/camera.py +0 -0
  14. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/cas.py +0 -0
  15. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/constants.py +0 -0
  16. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/light_bulb.py +0 -0
  17. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/mqtt.py +0 -0
  18. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/test_cam_rtsp.py +0 -0
  19. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi/utils.py +0 -0
  20. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi.egg-info/SOURCES.txt +0 -0
  21. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi.egg-info/dependency_links.txt +0 -0
  22. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi.egg-info/entry_points.txt +0 -0
  23. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi.egg-info/requires.txt +0 -0
  24. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/pyezvizapi.egg-info/top_level.txt +0 -0
  25. {pyezvizapi-1.0.1.3 → pyezvizapi-1.0.1.5}/setup.cfg +0 -0
@@ -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.5
4
4
  Summary: Pilot your Ezviz cameras
5
5
  Home-page: https://github.com/RenierM26/pyEzvizApi/
6
6
  Author: Renier Moorcroft
@@ -19,6 +19,7 @@ from .constants import (
19
19
  )
20
20
  from .exceptions import (
21
21
  AuthTestResultFailed,
22
+ DeviceException,
22
23
  EzvizAuthTokenExpired,
23
24
  EzvizAuthVerificationCode,
24
25
  HTTPError,
@@ -37,6 +38,7 @@ __all__ = [
37
38
  "BatteryCameraWorkMode",
38
39
  "DefenseModeType",
39
40
  "DeviceCatagories",
41
+ "DeviceException",
40
42
  "DeviceSwitchType",
41
43
  "DisplayMode",
42
44
  "EzvizAuthTokenExpired",
@@ -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/"
@@ -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,
@@ -69,6 +71,7 @@ from .constants import (
69
71
  MessageFilterType,
70
72
  )
71
73
  from .exceptions import (
74
+ DeviceException,
72
75
  EzvizAuthTokenExpired,
73
76
  EzvizAuthVerificationCode,
74
77
  HTTPError,
@@ -1271,7 +1274,7 @@ class EzvizClient:
1271
1274
  for item in devices.get("CLOUD", {})
1272
1275
  if devices["CLOUD"][item].get("deviceSerial") == _serial
1273
1276
  }
1274
- _res_id = _res_id_list.pop() if len(_res_id_list) else "NONE"
1277
+ _res_id = _res_id_list.pop() if _res_id_list else "NONE"
1275
1278
 
1276
1279
  result[_serial] = {
1277
1280
  "CLOUD": {_res_id: devices.get("CLOUD", {}).get(_res_id, {})},
@@ -1357,7 +1360,26 @@ class EzvizClient:
1357
1360
  def get_cam_key(
1358
1361
  self, serial: str, smscode: int | None = None, max_retries: int = 0
1359
1362
  ) -> Any:
1360
- """Get Camera encryption key. The key that is set after the camera is added to the account."""
1363
+ """Get Camera encryption key. The key that is set after the camera is added to the account.
1364
+
1365
+ Args:
1366
+ serial (str): The camera serial number.
1367
+ smscode (int | None): The 2FA code account when rights elevation is required.
1368
+ max_retries (int): The maximum number of retries. Defaults to 0.
1369
+
1370
+ Raises:
1371
+ PyEzvizError: If the camera encryption key can't be retrieved.
1372
+ EzvizAuthVerificationCode: If the account requires elevation with 2FA code.
1373
+ DeviceException: If the physical device is not reachable.
1374
+
1375
+ Returns:
1376
+ Any: JSON response, filtered to return encryptkey:
1377
+ {
1378
+ "resultCode": int, # Result code (0 if successful)
1379
+ "encryptkey": str, # Camera encryption key
1380
+ "resultDes": str # Status message in chinese
1381
+ }
1382
+ """
1361
1383
 
1362
1384
  if max_retries > MAX_RETRIES:
1363
1385
  raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
@@ -1401,6 +1423,9 @@ class EzvizClient:
1401
1423
  if json_output["resultCode"] == "20002":
1402
1424
  raise EzvizAuthVerificationCode(f"MFA code required: Got {json_output})")
1403
1425
 
1426
+ if json_output["resultCode"] == 2009:
1427
+ raise DeviceException(f"Device not reachable: Got {json_output})")
1428
+
1404
1429
  if json_output["resultCode"] != "0":
1405
1430
  if json_output["resultCode"] == "-1":
1406
1431
  _LOGGER.warning(
@@ -1421,9 +1446,34 @@ class EzvizClient:
1421
1446
  serial: str,
1422
1447
  encrypt_pwd: str | None = None,
1423
1448
  msg_auth_code: int | None = None,
1449
+ sender_type: int = 0,
1424
1450
  max_retries: int = 0,
1425
1451
  ) -> Any:
1426
- """Get Camera auth code. This is the verification code on the camera sticker."""
1452
+ """Get Camera auth code. This is the verification code on the camera sticker.
1453
+
1454
+ Args:
1455
+ serial (str): The camera serial number.
1456
+ encrypt_pwd (str | None): This is always none.
1457
+ msg_auth_code (int | None): The 2FA code.
1458
+ sender_type (int): The sender type. Defaults to 0. Needs to be 3 when returning 2FA code.
1459
+ max_retries (int): The maximum number of retries. Defaults to 0.
1460
+
1461
+ Raises:
1462
+ PyEzvizError: If the camera auth code cannot be retrieved.
1463
+ EzvizAuthVerificationCode: If the operation requires elevation with 2FA.
1464
+ DeviceException: If the physical device is not reachable.
1465
+
1466
+ Returns:
1467
+ Any: JSON response, filtered to return devAuthCode:
1468
+ {
1469
+ "devAuthCode": str, # Device authorization code
1470
+ "meta": {
1471
+ "code": int, # Status code (200 if successful)
1472
+ "message": str, # Status message in chinese
1473
+ "moreInfo": null or {"INVALID_PARAMETER": str}
1474
+ }
1475
+ }
1476
+ """
1427
1477
 
1428
1478
  if max_retries > MAX_RETRIES:
1429
1479
  raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
@@ -1431,7 +1481,7 @@ class EzvizClient:
1431
1481
  params: dict[str, int | str | None] = {
1432
1482
  "encrptPwd": encrypt_pwd,
1433
1483
  "msgAuthCode": msg_auth_code,
1434
- "senderType": 0,
1484
+ "senderType": sender_type,
1435
1485
  }
1436
1486
 
1437
1487
  try:
@@ -1464,13 +1514,88 @@ class EzvizClient:
1464
1514
  + str(req.text)
1465
1515
  ) from err
1466
1516
 
1517
+ if json_output["meta"]["code"] == 80000:
1518
+ raise EzvizAuthVerificationCode("Operation requires 2FA check")
1519
+
1520
+ if json_output["resultCode"] == 2009:
1521
+ raise DeviceException(f"Device not reachable: Got {json_output}")
1522
+
1467
1523
  if json_output["meta"]["code"] != 200:
1468
1524
  raise PyEzvizError(
1469
- f"Could not get camera verification key: Got {json_output})"
1525
+ f"Could not get camera verification key: Got {json_output}"
1470
1526
  )
1471
1527
 
1472
1528
  return json_output["devAuthCode"]
1473
1529
 
1530
+ def get_2fa_check_code(
1531
+ self,
1532
+ biz_type: str = "DEVICE_AUTH_CODE",
1533
+ username: str | None = None,
1534
+ max_retries: int = 0,
1535
+ ) -> Any:
1536
+ """Initiate 2FA check for sensitive operations. Elevates your session token permission.
1537
+
1538
+ Args:
1539
+ biz_type (str): The operation type. (DEVICE_ENCRYPTION | DEVICE_AUTH_CODE)
1540
+ username (str): The account username.
1541
+ max_retries (int): The maximum number of retries. Defaults to 0.
1542
+
1543
+ Raises:
1544
+ PyEzvizError: If the operation fails.
1545
+
1546
+ Returns:
1547
+ Any: JSON response with the following structure:
1548
+ {
1549
+ "meta": {
1550
+ "code": int, # Status code (200 if successful)
1551
+ "message": str # Status message in chinese
1552
+ "moreInfo": null
1553
+ },
1554
+ "contact": {
1555
+ "type": str, # 2FA code will be sent to this (EMAIL)
1556
+ "fuzzyContact": str # Destination value (e.g., someone@email.local)
1557
+ }
1558
+ }
1559
+ """
1560
+
1561
+ if max_retries > MAX_RETRIES:
1562
+ raise PyEzvizError("Can't gather proper data. Max retries exceeded.")
1563
+
1564
+ try:
1565
+ req = self._session.post(
1566
+ url=f"https://{self._token['api_url']}{API_ENDPOINT_2FA_VALIDATE_POST_AUTH}",
1567
+ data={"bizType": biz_type, "from": username},
1568
+ timeout=self._timeout,
1569
+ )
1570
+
1571
+ req.raise_for_status()
1572
+
1573
+ except requests.HTTPError as err:
1574
+ if err.response.status_code == 401:
1575
+ # session is wrong, need to relogin
1576
+ self.login()
1577
+ return self.get_2fa_check_code(biz_type, username, max_retries + 1)
1578
+
1579
+ raise HTTPError from err
1580
+
1581
+ try:
1582
+ json_output = req.json()
1583
+
1584
+ except ValueError as err:
1585
+ raise PyEzvizError(
1586
+ "Impossible to decode response: "
1587
+ + str(err)
1588
+ + "\nResponse was: "
1589
+ + str(req.text)
1590
+ ) from err
1591
+
1592
+ if json_output["meta"]["code"] != 200:
1593
+ raise PyEzvizError(
1594
+ f"Could not request elevated permission: Got {json_output})"
1595
+ )
1596
+
1597
+ return json_output
1598
+
1474
1599
  def create_panoramic(self, serial: str, max_retries: int = 0) -> Any:
1475
1600
  """Create panoramic image."""
1476
1601
 
@@ -1611,26 +1736,38 @@ class EzvizClient:
1611
1736
 
1612
1737
  return True
1613
1738
 
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
- })
1739
+ def remote_unlock(self, serial: str, user_id: str, lock_no: int) -> bool:
1740
+ """Sends a remote command to unlock a specific lock.
1627
1741
 
1742
+ Args:
1743
+ serial (str): The camera serial.
1744
+ user_id (str): The user id.
1745
+ lock_no (int): The lock number.
1746
+
1747
+ Raises:
1748
+ PyEzvizError: If max retries are exceeded or if the response indicates failure.
1749
+ HTTPError: If an HTTP error occurs (other than a 401, which triggers re-login).
1750
+
1751
+ Returns:
1752
+ bool: True if the operation was successful.
1753
+
1754
+ """
1755
+ try:
1628
1756
  headers = self._session.headers
1629
1757
  headers.update({"Content-Type": "application/json"})
1630
1758
 
1631
1759
  req = self._session.put(
1632
- url=f"https://{self._token['api_url']}{endpoint}",
1633
- data=payload,
1760
+ url=f"https://{self._token['api_url']}{API_ENDPOINT_IOT_ACTION}{serial}{API_ENDPOINT_REMOTE_UNLOCK}",
1761
+ data=json.dumps(
1762
+ {
1763
+ "unLockInfo": {
1764
+ "bindCode": f"{FEATURE_CODE}{user_id}",
1765
+ "lockNo": lock_no,
1766
+ "streamToken": "",
1767
+ "userName": user_id,
1768
+ }
1769
+ }
1770
+ ),
1634
1771
  timeout=self._timeout,
1635
1772
  headers=headers,
1636
1773
  )
@@ -27,3 +27,7 @@ class EzvizAuthTokenExpired(PyEzvizError):
27
27
 
28
28
  class EzvizAuthVerificationCode(PyEzvizError):
29
29
  """Authentication failed because MFA verification code is required."""
30
+
31
+
32
+ class DeviceException(PyEzvizError):
33
+ """The device network is abnormal, please check the device network or try again."""
@@ -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.5
4
4
  Summary: Pilot your Ezviz cameras
5
5
  Home-page: https://github.com/RenierM26/pyEzvizApi/
6
6
  Author: Renier Moorcroft
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name='pyezvizapi',
8
- version="1.0.1.3",
8
+ version="1.0.1.5",
9
9
  license='Apache Software License 2.0',
10
10
  author='Renier Moorcroft',
11
11
  author_email='RenierM26@users.github.com',
File without changes
File without changes
File without changes
File without changes
File without changes