pyezvizapi 1.0.3.3__tar.gz → 1.0.3.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.3.3/pyezvizapi.egg-info → pyezvizapi-1.0.3.4}/PKG-INFO +1 -1
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/__init__.py +18 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/feature.py +192 -1
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4/pyezvizapi.egg-info}/PKG-INFO +1 -1
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/setup.py +1 -1
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/LICENSE +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/LICENSE.md +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/MANIFEST.in +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/README.md +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/__main__.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/api_endpoints.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/camera.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/cas.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/client.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/constants.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/exceptions.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/light_bulb.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/models.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/mqtt.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/test_cam_rtsp.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/test_mqtt.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi/utils.py +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi.egg-info/SOURCES.txt +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi.egg-info/dependency_links.txt +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi.egg-info/entry_points.txt +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi.egg-info/requires.txt +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/pyezvizapi.egg-info/top_level.txt +0 -0
- {pyezvizapi-1.0.3.3 → pyezvizapi-1.0.3.4}/setup.cfg +0 -0
|
@@ -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,13 @@ 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
|
+
support_ext_value,
|
|
52
61
|
)
|
|
53
62
|
from .light_bulb import EzvizLightBulb
|
|
54
63
|
from .models import EzvizDeviceRecord, build_device_records_map
|
|
@@ -91,7 +100,11 @@ __all__ = [
|
|
|
91
100
|
"day_night_sensitivity_value",
|
|
92
101
|
"device_icr_dss_config",
|
|
93
102
|
"display_mode_value",
|
|
103
|
+
"get_algorithm_value",
|
|
104
|
+
"has_algorithm_subtype",
|
|
94
105
|
"has_osd_overlay",
|
|
106
|
+
"iter_algorithm_entries",
|
|
107
|
+
"iter_channel_algorithm_entries",
|
|
95
108
|
"lens_defog_config",
|
|
96
109
|
"lens_defog_value",
|
|
97
110
|
"night_vision_config",
|
|
@@ -99,6 +112,11 @@ __all__ = [
|
|
|
99
112
|
"night_vision_luminance_value",
|
|
100
113
|
"night_vision_mode_value",
|
|
101
114
|
"night_vision_payload",
|
|
115
|
+
"normalize_port_security",
|
|
102
116
|
"optionals_mapping",
|
|
117
|
+
"port_security_config",
|
|
118
|
+
"port_security_has_port",
|
|
119
|
+
"port_security_port_enabled",
|
|
103
120
|
"resolve_channel",
|
|
121
|
+
"support_ext_value",
|
|
104
122
|
]
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from collections.abc import Mapping, MutableMapping
|
|
5
|
+
from collections.abc import Iterable, Iterator, Mapping, MutableMapping
|
|
6
6
|
from typing import Any, cast
|
|
7
7
|
|
|
8
8
|
from .utils import coerce_int, decode_json
|
|
@@ -78,6 +78,197 @@ def optionals_mapping(camera_data: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
78
78
|
return dict(optionals) if isinstance(optionals, Mapping) else {}
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def optionals_dict(camera_data: Mapping[str, Any]) -> dict[str, Any]:
|
|
82
|
+
"""Return convenience wrapper for optionals mapping."""
|
|
83
|
+
|
|
84
|
+
return optionals_mapping(camera_data)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def iter_algorithm_entries(camera_data: Mapping[str, Any]) -> Iterator[dict[str, Any]]:
|
|
88
|
+
"""Yield entries from the AlgorithmInfo optionals list."""
|
|
89
|
+
|
|
90
|
+
entries = optionals_dict(camera_data).get("AlgorithmInfo")
|
|
91
|
+
if not isinstance(entries, Iterable):
|
|
92
|
+
return
|
|
93
|
+
for entry in entries:
|
|
94
|
+
if isinstance(entry, Mapping):
|
|
95
|
+
yield dict(entry)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def iter_channel_algorithm_entries(
|
|
99
|
+
camera_data: Mapping[str, Any], channel: int
|
|
100
|
+
) -> Iterator[dict[str, Any]]:
|
|
101
|
+
"""Yield AlgorithmInfo entries filtered by channel."""
|
|
102
|
+
|
|
103
|
+
for entry in iter_algorithm_entries(camera_data):
|
|
104
|
+
entry_channel = coerce_int(entry.get("channel")) or 1
|
|
105
|
+
if entry_channel == channel:
|
|
106
|
+
yield entry
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_algorithm_value(
|
|
110
|
+
camera_data: Mapping[str, Any], subtype: str, channel: int
|
|
111
|
+
) -> int | None:
|
|
112
|
+
"""Return AlgorithmInfo value for provided subtype/channel."""
|
|
113
|
+
|
|
114
|
+
for entry in iter_channel_algorithm_entries(camera_data, channel):
|
|
115
|
+
if entry.get("SubType") != subtype:
|
|
116
|
+
continue
|
|
117
|
+
return coerce_int(entry.get("Value"))
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def has_algorithm_subtype(
|
|
122
|
+
camera_data: Mapping[str, Any], subtype: str, channel: int = 1
|
|
123
|
+
) -> bool:
|
|
124
|
+
"""Return True when AlgorithmInfo contains subtype for channel."""
|
|
125
|
+
|
|
126
|
+
return get_algorithm_value(camera_data, subtype, channel) is not None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def support_ext_value(camera_data: Mapping[str, Any], ext_key: str) -> str | None:
|
|
130
|
+
"""Fetch a supportExt entry as a string when present."""
|
|
131
|
+
|
|
132
|
+
raw = camera_data.get("supportExt")
|
|
133
|
+
if not isinstance(raw, Mapping):
|
|
134
|
+
device_infos = camera_data.get("deviceInfos")
|
|
135
|
+
if isinstance(device_infos, Mapping):
|
|
136
|
+
raw = device_infos.get("supportExt")
|
|
137
|
+
|
|
138
|
+
if not isinstance(raw, Mapping):
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
value = raw.get(ext_key)
|
|
142
|
+
return str(value) if value is not None else None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _normalize_port_list(value: Any) -> list[dict[str, Any]] | None:
|
|
146
|
+
"""Decode a list of port-security entries."""
|
|
147
|
+
|
|
148
|
+
value = decode_json(value)
|
|
149
|
+
if not isinstance(value, Iterable):
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
normalized: list[dict[str, Any]] = []
|
|
153
|
+
for entry in value:
|
|
154
|
+
entry = decode_json(entry)
|
|
155
|
+
if not isinstance(entry, Mapping):
|
|
156
|
+
return None
|
|
157
|
+
port = coerce_int(entry.get("portNo"))
|
|
158
|
+
if port is None:
|
|
159
|
+
continue
|
|
160
|
+
normalized.append({"portNo": port, "enabled": bool(entry.get("enabled"))})
|
|
161
|
+
|
|
162
|
+
return normalized
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def normalize_port_security(payload: Any) -> dict[str, Any]:
|
|
166
|
+
"""Normalize IoT port-security payloads."""
|
|
167
|
+
|
|
168
|
+
seen: set[int] = set()
|
|
169
|
+
|
|
170
|
+
def _walk(obj: Any, hint: bool | None = None) -> dict[str, Any] | None:
|
|
171
|
+
obj = decode_json(obj)
|
|
172
|
+
if obj is None:
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
if isinstance(obj, Mapping):
|
|
176
|
+
obj_id = id(obj)
|
|
177
|
+
if obj_id in seen:
|
|
178
|
+
return None
|
|
179
|
+
seen.add(obj_id)
|
|
180
|
+
|
|
181
|
+
enabled_local = obj.get("enabled")
|
|
182
|
+
if isinstance(enabled_local, bool):
|
|
183
|
+
hint = enabled_local
|
|
184
|
+
|
|
185
|
+
ports = _normalize_port_list(obj.get("portSecurityList"))
|
|
186
|
+
if ports is not None:
|
|
187
|
+
return {
|
|
188
|
+
"portSecurityList": ports,
|
|
189
|
+
"enabled": bool(enabled_local)
|
|
190
|
+
if isinstance(enabled_local, bool)
|
|
191
|
+
else bool(hint)
|
|
192
|
+
if isinstance(hint, bool)
|
|
193
|
+
else True,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for key in (
|
|
197
|
+
"PortSecurity",
|
|
198
|
+
"value",
|
|
199
|
+
"data",
|
|
200
|
+
"NetworkSecurityProtection",
|
|
201
|
+
):
|
|
202
|
+
if key in obj:
|
|
203
|
+
candidate = _walk(obj[key], hint)
|
|
204
|
+
if candidate:
|
|
205
|
+
if "enabled" not in candidate and isinstance(hint, bool):
|
|
206
|
+
candidate["enabled"] = hint
|
|
207
|
+
return candidate
|
|
208
|
+
|
|
209
|
+
for value in obj.values():
|
|
210
|
+
candidate = _walk(value, hint)
|
|
211
|
+
if candidate:
|
|
212
|
+
if "enabled" not in candidate and isinstance(hint, bool):
|
|
213
|
+
candidate["enabled"] = hint
|
|
214
|
+
return candidate
|
|
215
|
+
|
|
216
|
+
elif isinstance(obj, Iterable):
|
|
217
|
+
for item in obj:
|
|
218
|
+
candidate = _walk(item, hint)
|
|
219
|
+
if candidate:
|
|
220
|
+
return candidate
|
|
221
|
+
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
normalized = _walk(payload)
|
|
225
|
+
if isinstance(normalized, dict):
|
|
226
|
+
normalized.setdefault("enabled", True)
|
|
227
|
+
return normalized
|
|
228
|
+
return {}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def port_security_config(camera_data: Mapping[str, Any]) -> dict[str, Any]:
|
|
232
|
+
"""Return the normalized port-security mapping for a camera payload."""
|
|
233
|
+
|
|
234
|
+
direct = camera_data.get("NetworkSecurityProtection")
|
|
235
|
+
normalized = normalize_port_security(direct)
|
|
236
|
+
if normalized:
|
|
237
|
+
return normalized
|
|
238
|
+
|
|
239
|
+
feature = camera_data.get("FEATURE_INFO")
|
|
240
|
+
if isinstance(feature, Mapping):
|
|
241
|
+
normalized = normalize_port_security(feature)
|
|
242
|
+
if normalized:
|
|
243
|
+
return normalized
|
|
244
|
+
|
|
245
|
+
return {}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def port_security_has_port(camera_data: Mapping[str, Any], port: int) -> bool:
|
|
249
|
+
"""Return True if the normalized config contains the port."""
|
|
250
|
+
|
|
251
|
+
ports = port_security_config(camera_data).get("portSecurityList")
|
|
252
|
+
if not isinstance(ports, Iterable):
|
|
253
|
+
return False
|
|
254
|
+
return any(
|
|
255
|
+
isinstance(entry, Mapping) and coerce_int(entry.get("portNo")) == port
|
|
256
|
+
for entry in ports
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def port_security_port_enabled(camera_data: Mapping[str, Any], port: int) -> bool:
|
|
261
|
+
"""Return True if the specific port is enabled."""
|
|
262
|
+
|
|
263
|
+
ports = port_security_config(camera_data).get("portSecurityList")
|
|
264
|
+
if not isinstance(ports, Iterable):
|
|
265
|
+
return False
|
|
266
|
+
for entry in ports:
|
|
267
|
+
if isinstance(entry, Mapping) and coerce_int(entry.get("portNo")) == port:
|
|
268
|
+
return bool(entry.get("enabled"))
|
|
269
|
+
return False
|
|
270
|
+
|
|
271
|
+
|
|
81
272
|
def display_mode_value(camera_data: Mapping[str, Any]) -> int:
|
|
82
273
|
"""Return display mode value (1..3) from camera data."""
|
|
83
274
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|