pycloudedge 0.1.4.dev1__tar.gz → 0.1.4.dev2__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.
Files changed (33) hide show
  1. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/PKG-INFO +1 -1
  2. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/_version.py +3 -3
  3. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/client.py +100 -37
  4. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/PKG-INFO +1 -1
  5. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/.env.example +0 -0
  6. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/.gitignore +0 -0
  7. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/LICENSE +0 -0
  8. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/MANIFEST.in +0 -0
  9. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/README.md +0 -0
  10. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/__init__.py +0 -0
  11. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/cli.py +0 -0
  12. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/constants.py +0 -0
  13. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/exceptions.py +0 -0
  14. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/iot_parameters.py +0 -0
  15. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/logging_config.py +0 -0
  16. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/utils.py +0 -0
  17. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/cloudedge/validators.py +0 -0
  18. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/examples/README.md +0 -0
  19. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/examples/basic_example.py +0 -0
  20. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/examples/device_control.py +0 -0
  21. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/examples/network_ping_status.py +0 -0
  22. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/SOURCES.txt +0 -0
  23. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/dependency_links.txt +0 -0
  24. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/entry_points.txt +0 -0
  25. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/requires.txt +0 -0
  26. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pycloudedge.egg-info/top_level.txt +0 -0
  27. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/pyproject.toml +0 -0
  28. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/requirements-dev.txt +0 -0
  29. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/requirements.txt +0 -0
  30. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/setup.cfg +0 -0
  31. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/setup.py +0 -0
  32. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/tests/test_basic.py +0 -0
  33. {pycloudedge-0.1.4.dev1 → pycloudedge-0.1.4.dev2}/tests/test_improvements.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycloudedge
3
- Version: 0.1.4.dev1
3
+ Version: 0.1.4.dev2
4
4
  Summary: Python library for CloudEdge cameras
5
5
  Home-page: https://github.com/fradaloisio/pycloudedge
6
6
  Author: Francesco D'Aloisio
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.4.dev1'
32
- __version_tuple__ = version_tuple = (0, 1, 4, 'dev1')
31
+ __version__ = version = '0.1.4.dev2'
32
+ __version_tuple__ = version_tuple = (0, 1, 4, 'dev2')
33
33
 
34
- __commit_id__ = commit_id = 'gbd742badc'
34
+ __commit_id__ = commit_id = 'gf40c10343'
@@ -44,6 +44,54 @@ from .validators import validate_email, validate_country_code, validate_phone_co
44
44
  from .logging_config import get_logger
45
45
  from .utils import retry_on_failure
46
46
 
47
+ # API list keys → readable product type when deviceTypeName is a CDN image URL
48
+ _DEVICE_LIST_CATEGORY_LABELS = {
49
+ "snap": "Camera",
50
+ "ipc": "Camera",
51
+ "nvr": "NVR",
52
+ "doorbell": "Doorbell",
53
+ "chime": "Chime",
54
+ }
55
+
56
+
57
+ def _device_icon_url_from_type_name(device_type_name: Any) -> Optional[str]:
58
+ """Return URL if deviceTypeName is an http(s) icon URL (Meari/OSS), else None."""
59
+ if not isinstance(device_type_name, str):
60
+ return None
61
+ s = device_type_name.strip()
62
+ if s.startswith(("http://", "https://")):
63
+ return s
64
+ return None
65
+
66
+
67
+ def _human_type_and_icon_url(
68
+ device: Dict[str, Any],
69
+ list_category: Optional[str] = None,
70
+ ) -> tuple[str, Optional[str]]:
71
+ """
72
+ CloudEdge often puts a product image URL in deviceTypeName instead of a label.
73
+ Return a human-readable type for UIs (e.g. Home Assistant model) and optional icon URL.
74
+ """
75
+ raw = device.get("deviceTypeName")
76
+ icon_url = _device_icon_url_from_type_name(raw)
77
+ if icon_url is not None:
78
+ label = _DEVICE_LIST_CATEGORY_LABELS.get(list_category or "", "SmartEye Camera")
79
+ lower = icon_url.lower()
80
+ if "doorbell" in lower:
81
+ label = "Doorbell"
82
+ elif "chime" in lower:
83
+ label = "Chime"
84
+ elif "nvr" in lower:
85
+ label = "NVR"
86
+ elif "snap" in lower or "ipc" in lower:
87
+ label = "Camera"
88
+ return label, icon_url
89
+ if raw is not None and str(raw).strip():
90
+ return str(raw).strip(), None
91
+ if list_category:
92
+ return _DEVICE_LIST_CATEGORY_LABELS.get(list_category, "Unknown"), None
93
+ return "Unknown", None
94
+
47
95
 
48
96
  class CloudEdgeClient:
49
97
  """
@@ -787,15 +835,19 @@ class CloudEdgeClient:
787
835
  device_list = response_data[device_type]
788
836
  if isinstance(device_list, list):
789
837
  for device in device_list:
838
+ type_str, icon_url = _human_type_and_icon_url(
839
+ device, list_category=device_type
840
+ )
790
841
  device_dict = {
791
842
  'device_id': device.get('deviceID'),
792
843
  'serial_number': device.get('snNum'),
793
844
  'name': device.get('deviceName', 'Unnamed'),
794
- 'type': device.get('deviceTypeName', 'Unknown'),
845
+ 'type': type_str,
795
846
  'type_id': device.get('devTypeID'),
796
847
  'host_key': device.get('hostKey'),
797
848
  'online': device.get('devStatus') == 1, # Store original API status
798
- 'home_id': home_id
849
+ 'home_id': home_id,
850
+ 'device_icon_url': icon_url,
799
851
  }
800
852
 
801
853
  # Get enhanced online status
@@ -920,43 +972,54 @@ class CloudEdgeClient:
920
972
  import json
921
973
  self._log(f"API Response structure: {json.dumps(response_data, indent=2)}")
922
974
 
923
- devices = []
924
-
925
- # Check for devices in different device type keys (working format)
926
975
  device_types = ['nvr', 'ipc', 'chime', 'doorbell', 'snap']
927
-
928
- for device_type in device_types:
929
- if device_type in response_data and response_data[device_type]:
930
- device_list = response_data[device_type]
931
- if isinstance(device_list, list):
932
- self._log(f"Found {len(device_list)} devices under '{device_type}' key")
933
- devices.extend(device_list)
934
-
935
- # Fallback: check for devices in result.deviceList (older format)
936
- if not devices:
937
- device_list = response_data.get("result", {}).get("deviceList", [])
938
- if isinstance(device_list, list) and device_list:
939
- self._log(f"Found {len(device_list)} devices under 'result.deviceList' key")
940
- devices.extend(device_list)
941
-
942
- # Convert to standardized format
943
976
  standardized_devices = []
944
- for device in devices:
945
- device_dict = {
946
- 'device_id': device.get('deviceID'),
947
- 'serial_number': device.get('snNum'),
948
- 'name': device.get('deviceName', 'Unnamed'),
949
- 'type': device.get('deviceTypeName', 'Unknown'),
950
- 'type_id': device.get('devTypeID'),
951
- 'host_key': device.get('hostKey'),
952
- 'online': device.get('onLine') == 1 # Store original API status
953
- }
954
-
955
- # Get enhanced online status
956
- device_dict['online'] = self._get_enhanced_device_status(device_dict)
957
-
958
- standardized_devices.append(device_dict)
959
-
977
+
978
+ # Modern API: devices grouped by category key
979
+ for device_type in device_types:
980
+ device_list = response_data.get(device_type)
981
+ if not isinstance(device_list, list) or not device_list:
982
+ continue
983
+ self._log(f"Found {len(device_list)} devices under '{device_type}' key")
984
+ for device in device_list:
985
+ type_str, icon_url = _human_type_and_icon_url(
986
+ device, list_category=device_type
987
+ )
988
+ device_dict = {
989
+ 'device_id': device.get('deviceID'),
990
+ 'serial_number': device.get('snNum'),
991
+ 'name': device.get('deviceName', 'Unnamed'),
992
+ 'type': type_str,
993
+ 'type_id': device.get('devTypeID'),
994
+ 'host_key': device.get('hostKey'),
995
+ 'online': device.get('onLine') == 1,
996
+ 'device_icon_url': icon_url,
997
+ }
998
+ device_dict['online'] = self._get_enhanced_device_status(device_dict)
999
+ standardized_devices.append(device_dict)
1000
+
1001
+ # Older API fallback: devices under result.deviceList
1002
+ if not standardized_devices:
1003
+ fallback_list = response_data.get("result", {}).get("deviceList", [])
1004
+ if isinstance(fallback_list, list) and fallback_list:
1005
+ self._log(f"Found {len(fallback_list)} devices under 'result.deviceList' key")
1006
+ for device in fallback_list:
1007
+ type_str, icon_url = _human_type_and_icon_url(
1008
+ device, list_category=None
1009
+ )
1010
+ device_dict = {
1011
+ 'device_id': device.get('deviceID'),
1012
+ 'serial_number': device.get('snNum'),
1013
+ 'name': device.get('deviceName', 'Unnamed'),
1014
+ 'type': type_str,
1015
+ 'type_id': device.get('devTypeID'),
1016
+ 'host_key': device.get('hostKey'),
1017
+ 'online': device.get('onLine') == 1,
1018
+ 'device_icon_url': icon_url,
1019
+ }
1020
+ device_dict['online'] = self._get_enhanced_device_status(device_dict)
1021
+ standardized_devices.append(device_dict)
1022
+
960
1023
  return standardized_devices
961
1024
  else:
962
1025
  error_msg = response_data.get('resultMsg', 'Unknown error')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycloudedge
3
- Version: 0.1.4.dev1
3
+ Version: 0.1.4.dev2
4
4
  Summary: Python library for CloudEdge cameras
5
5
  Home-page: https://github.com/fradaloisio/pycloudedge
6
6
  Author: Francesco D'Aloisio