pyezvizapi 1.0.3.3__py3-none-any.whl → 1.0.4.3__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.
pyezvizapi/utils.py CHANGED
@@ -2,17 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Iterable, Iterator
5
6
  import datetime
6
7
  from hashlib import md5
7
8
  import json
8
9
  import logging
9
10
  import re as _re
10
11
  from typing import Any
11
- import uuid
12
12
  from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
13
13
 
14
14
  from Crypto.Cipher import AES
15
15
 
16
+ from .constants import HIK_ENCRYPTION_HEADER
16
17
  from .exceptions import PyEzvizError
17
18
 
18
19
  _LOGGER = logging.getLogger(__name__)
@@ -62,18 +63,64 @@ def convert_to_dict(data: Any) -> Any:
62
63
 
63
64
  def string_to_list(data: Any, separator: str = ",") -> Any:
64
65
  """Convert a string representation of a list to a list."""
65
- if isinstance(data, str):
66
- if separator in data:
67
- try:
68
- # Attempt to convert the string into a list
69
- return data.split(separator)
66
+ if isinstance(data, str) and separator in data:
67
+ try:
68
+ # Attempt to convert the string into a list
69
+ return data.split(separator)
70
70
 
71
- except AttributeError:
72
- return data
71
+ except AttributeError:
72
+ return data
73
73
 
74
74
  return data
75
75
 
76
76
 
77
+ PathComponent = str | int
78
+ WILDCARD_STEP = "*"
79
+ _MISSING = object()
80
+ MILLISECONDS_THRESHOLD = 1e11
81
+
82
+
83
+ def iter_nested(data: Any, path: Iterable[PathComponent]) -> Iterator[Any]:
84
+ """Yield values reachable by following a dotted path with optional wildcards."""
85
+
86
+ current: list[Any] = [data]
87
+
88
+ for step in path:
89
+ next_level: list[Any] = []
90
+ for candidate in current:
91
+ if step == WILDCARD_STEP:
92
+ if isinstance(candidate, dict):
93
+ next_level.extend(candidate.values())
94
+ elif isinstance(candidate, (list, tuple)):
95
+ next_level.extend(candidate)
96
+ continue
97
+
98
+ if isinstance(candidate, dict) and step in candidate:
99
+ next_level.append(candidate[step])
100
+ continue
101
+
102
+ if (
103
+ isinstance(candidate, (list, tuple))
104
+ and isinstance(step, int)
105
+ and -len(candidate) <= step < len(candidate)
106
+ ):
107
+ next_level.append(candidate[step])
108
+
109
+ current = next_level
110
+ if not current:
111
+ break
112
+
113
+ yield from current
114
+
115
+
116
+ def first_nested(
117
+ data: Any, path: Iterable[PathComponent], default: Any = None
118
+ ) -> Any:
119
+ """Return the first value produced by iter_nested or ``default``."""
120
+
121
+ return next(iter_nested(data, path), default)
122
+
123
+
77
124
  def fetch_nested_value(data: Any, keys: list, default_value: Any = None) -> Any:
78
125
  """Fetch the value corresponding to the given nested keys in a dictionary.
79
126
 
@@ -88,14 +135,8 @@ def fetch_nested_value(data: Any, keys: list, default_value: Any = None) -> Any:
88
135
  The value corresponding to the nested keys or the default value.
89
136
 
90
137
  """
91
- try:
92
- for key in keys:
93
- data = data[key]
94
-
95
- except (KeyError, TypeError):
96
- return default_value
97
-
98
- return data
138
+ value = first_nested(data, keys, _MISSING)
139
+ return default_value if value is _MISSING else value
99
140
 
100
141
 
101
142
  def decrypt_image(input_data: bytes, password: str) -> bytes:
@@ -112,37 +153,102 @@ def decrypt_image(input_data: bytes, password: str) -> bytes:
112
153
  bytes: Decrypted image data
113
154
 
114
155
  """
115
- if len(input_data) < 48:
156
+ header_len = len(HIK_ENCRYPTION_HEADER)
157
+ min_length = header_len + 32 # header + md5 hash
158
+
159
+ if len(input_data) < min_length:
116
160
  raise PyEzvizError("Invalid image data")
117
161
 
118
- # check header
119
- if input_data[:16] != b"hikencodepicture":
120
- _LOGGER.debug("Image header doesn't contain 'hikencodepicture'")
162
+ header_index = input_data.find(HIK_ENCRYPTION_HEADER)
163
+ if header_index == -1:
164
+ _LOGGER.debug("Image header doesn't contain %s", HIK_ENCRYPTION_HEADER)
121
165
  return input_data
122
166
 
123
- file_hash = input_data[16:48]
167
+ if header_index:
168
+ _LOGGER.debug("Image header found at offset %s, trimming preamble", header_index)
169
+ input_data = input_data[header_index:]
170
+ if len(input_data) < min_length:
171
+ raise PyEzvizError("Invalid image data after trimming preamble")
172
+
173
+ hash_end = header_len + 32
174
+ blocks = _split_encrypted_blocks(input_data, header_len, min_length)
175
+ if not blocks:
176
+ raise PyEzvizError("Invalid image data")
177
+
178
+ decrypted_parts = [
179
+ _decrypt_single_block(block, password, header_len, hash_end) for block in blocks
180
+ ]
181
+ if len(decrypted_parts) > 1:
182
+ _LOGGER.debug("Decrypted %s concatenated image blocks", len(decrypted_parts))
183
+ return b"".join(decrypted_parts)
184
+
185
+
186
+ def _split_encrypted_blocks(
187
+ data: bytes, header_len: int, min_length: int
188
+ ) -> list[bytes]:
189
+ """Split concatenated hikencodepicture segments into individual blocks."""
190
+ blocks: list[bytes] = []
191
+ cursor = 0
192
+ data_len = len(data)
193
+
194
+ while cursor <= data_len - min_length:
195
+ if data[cursor : cursor + header_len] != HIK_ENCRYPTION_HEADER:
196
+ next_header = data.find(HIK_ENCRYPTION_HEADER, cursor + 1)
197
+ if next_header == -1:
198
+ break
199
+ cursor = next_header
200
+ continue
201
+
202
+ next_header = data.find(HIK_ENCRYPTION_HEADER, cursor + header_len)
203
+ block = data[cursor : next_header if next_header != -1 else data_len]
204
+ if len(block) < min_length:
205
+ break
206
+ blocks.append(block)
207
+ if next_header == -1:
208
+ break
209
+ cursor = next_header
210
+
211
+ return blocks
212
+
213
+
214
+ def _decrypt_single_block(
215
+ block: bytes, password: str, header_len: int, hash_end: int
216
+ ) -> bytes:
217
+ """Decrypt a single hikencodepicture block."""
218
+ file_hash = block[header_len:hash_end]
124
219
  passwd_hash = md5(str.encode(md5(str.encode(password)).hexdigest())).hexdigest()
125
220
  if file_hash != str.encode(passwd_hash):
126
221
  raise PyEzvizError("Invalid password")
127
222
 
223
+ ciphertext = block[hash_end:]
224
+ if not ciphertext:
225
+ raise PyEzvizError("Missing ciphertext payload")
226
+
227
+ remainder = len(ciphertext) % AES.block_size
228
+ if remainder:
229
+ _LOGGER.debug(
230
+ "Ciphertext not aligned to 16 bytes; trimming %s trailing bytes", remainder
231
+ )
232
+ ciphertext = ciphertext[:-remainder]
233
+ if not ciphertext:
234
+ raise PyEzvizError("Ciphertext too short after alignment adjustment")
235
+
128
236
  key = str.encode(password.ljust(16, "\u0000")[:16])
129
237
  iv_code = bytes([48, 49, 50, 51, 52, 53, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0])
130
238
  cipher = AES.new(key, AES.MODE_CBC, iv_code)
131
239
 
132
- next_chunk = b""
133
- output_data = b""
134
- finished = False
135
- i = 48 # offset hikencodepicture + hash
136
240
  chunk_size = 1024 * AES.block_size
137
- while not finished:
138
- chunk, next_chunk = next_chunk, cipher.decrypt(input_data[i : i + chunk_size])
139
- if len(next_chunk) == 0:
140
- padding_length = chunk[-1]
141
- chunk = chunk[:-padding_length]
142
- finished = True
143
- output_data += chunk
144
- i += chunk_size
145
- return output_data
241
+ output_data = bytearray()
242
+
243
+ for start in range(0, len(ciphertext), chunk_size):
244
+ block_chunk = cipher.decrypt(ciphertext[start : start + chunk_size])
245
+ if start + chunk_size >= len(ciphertext):
246
+ padding_length = block_chunk[-1]
247
+ block_chunk = block_chunk[:-padding_length]
248
+ output_data.extend(block_chunk)
249
+
250
+ return bytes(output_data)
251
+
146
252
 
147
253
 
148
254
  def return_password_hash(password: str) -> str:
@@ -195,30 +301,6 @@ def deep_merge(dict1: Any, dict2: Any) -> Any:
195
301
  return merged
196
302
 
197
303
 
198
- def generate_unique_code() -> str:
199
- """Generate a deterministic, platform-agnostic unique code for the current host.
200
-
201
- This function retrieves the host's MAC address using Python's standard
202
- `uuid.getnode()` (works on Windows, Linux, macOS), converts it to a
203
- canonical string representation, and then hashes it using MD5 to produce
204
- a fixed-length hexadecimal string.
205
-
206
- Returns:
207
- str: A 32-character hexadecimal string uniquely representing
208
- the host's MAC address. For example:
209
- 'a94e6756hghjgfghg49e0f310d9e44a'.
210
-
211
- Notes:
212
- - The output is deterministic: the same machine returns the same code.
213
- - If the MAC address changes (e.g., different network adapter),
214
- the output will change.
215
- - MD5 is used here only for ID generation, not for security.
216
- """
217
- mac_int = uuid.getnode()
218
- mac_str = ":".join(f"{(mac_int >> i) & 0xFF:02x}" for i in range(40, -1, -8))
219
- return md5(mac_str.encode("utf-8")).hexdigest()
220
-
221
-
222
304
  # ---------------------------------------------------------------------------
223
305
  # Time helpers for alarm/motion handling
224
306
  # ---------------------------------------------------------------------------
@@ -255,7 +337,7 @@ def normalize_alarm_time(
255
337
  if epoch is not None:
256
338
  try:
257
339
  ts = float(epoch if not isinstance(epoch, str) else float(epoch))
258
- if ts > 1e11: # milliseconds
340
+ if ts > MILLISECONDS_THRESHOLD: # milliseconds
259
341
  ts /= 1000.0
260
342
  event_utc = datetime.datetime.fromtimestamp(ts, tz=datetime.UTC)
261
343
  alarm_dt_local = event_utc.astimezone(tzinfo)
@@ -322,10 +404,11 @@ def compute_motion_from_alarm(
322
404
  now_local = datetime.datetime.now(tz=tzinfo).replace(microsecond=0)
323
405
  now_utc = datetime.datetime.now(tz=datetime.UTC).replace(microsecond=0)
324
406
 
325
- if alarm_dt_utc is not None:
326
- delta = now_utc - alarm_dt_utc
327
- else:
328
- delta = now_local - alarm_dt_local
407
+ delta = (
408
+ now_utc - alarm_dt_utc
409
+ if alarm_dt_utc is not None
410
+ else now_local - alarm_dt_local
411
+ )
329
412
 
330
413
  seconds = float(delta.total_seconds())
331
414
  if seconds < 0:
@@ -0,0 +1,286 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyezvizapi
3
+ Version: 1.0.4.3
4
+ Summary: EZVIZ API client for Home Assistant and CLI
5
+ Home-page: https://github.com/RenierM26/pyEzvizApi/
6
+ Author: Renier Moorcroft
7
+ Author-email: RenierM26@users.github.com
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ License-File: LICENSE.md
12
+ Requires-Dist: requests
13
+ Requires-Dist: xmltodict
14
+ Requires-Dist: pycryptodome
15
+ Requires-Dist: paho-mqtt
16
+ Requires-Dist: pandas
17
+ Provides-Extra: dev
18
+ Requires-Dist: ruff; extra == "dev"
19
+ Requires-Dist: mypy; extra == "dev"
20
+ Requires-Dist: pytest; extra == "dev"
21
+ Requires-Dist: types-requests; extra == "dev"
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: home-page
25
+ Dynamic: license-file
26
+ Dynamic: requires-python
27
+
28
+ # Ezviz PyPi
29
+
30
+ ![Upload Python Package](https://github.com/RenierM26/pyEzvizApi/workflows/Upload%20Python%20Package/badge.svg)
31
+
32
+ ## Overview
33
+
34
+ Pilot your Ezviz cameras (and light bulbs) with this module. It is used by:
35
+
36
+ - The official Ezviz integration in Home Assistant
37
+ - The EZVIZ (Beta) custom integration for Home Assistant
38
+
39
+ You can also use it directly from the command line for quick checks and scripting.
40
+
41
+ ## Features
42
+
43
+ - Inspect device and connection status in table or JSON form
44
+ - Control cameras: PTZ, privacy/sleep/audio/IR/state LEDs, alarm settings
45
+ - Control light bulbs: toggle, status, brightness and color temperature
46
+ - Dump raw pagelist and device infos JSON for exploration/debugging
47
+ - Reuse a saved session token (no credentials needed after first login)
48
+
49
+ ## Install
50
+
51
+ From PyPI:
52
+
53
+ ```bash
54
+ pip install pyezvizapi
55
+ ```
56
+
57
+ After installation, a `pyezvizapi` command is available on your PATH.
58
+
59
+ ### Dependencies (development/local usage)
60
+
61
+ If you are running from a clone of this repository or using the helper scripts directly, ensure these packages are available:
62
+
63
+ ```bash
64
+ pip install requests paho-mqtt pycryptodome pandas
65
+ ```
66
+
67
+ ## Quick Start
68
+
69
+ ```bash
70
+ # See available commands and options
71
+ pyezvizapi --help
72
+
73
+ # First-time login and save token for reuse
74
+ pyezvizapi -u YOUR_EZVIZ_USERNAME -p YOUR_EZVIZ_PASSWORD --save-token devices status
75
+
76
+ # Subsequent runs can reuse the saved token (no credentials needed)
77
+ pyezvizapi devices status --json
78
+ ```
79
+
80
+ ## CLI Authentication
81
+
82
+ - Username/password: `-u/--username` and `-p/--password`
83
+ - Token file: `--token-file` (defaults to `ezviz_token.json` in the current directory)
84
+ - Save token: `--save-token` writes the current token after login
85
+ - MFA: The CLI prompts for a code if required by your account
86
+ - Region: `-r/--region` overrides the default region (`apiieu.ezvizlife.com`)
87
+
88
+ Examples:
89
+
90
+ ```bash
91
+ # First-time login and save token locally
92
+ pyezvizapi -u YOUR_EZVIZ_USERNAME -p YOUR_EZVIZ_PASSWORD --save-token devices status
93
+
94
+ # Reuse saved token (no credentials)
95
+ pyezvizapi devices status --json
96
+ ```
97
+
98
+ ## Output Modes
99
+
100
+ - Default: human-readable tables (for list/status views)
101
+ - JSON: add `--json` for easy parsing and editor-friendly exploration
102
+
103
+ ## CLI Commands
104
+
105
+ All commands are subcommands of the module runner:
106
+
107
+ ```bash
108
+ pyezvizapi <command> [options]
109
+ ```
110
+
111
+ ### devices
112
+
113
+ - Actions: `device`, `status`, `switch`, `connection`
114
+ - Examples:
115
+
116
+ ```bash
117
+ # Table view
118
+ pyezvizapi devices status
119
+
120
+ # JSON view
121
+ pyezvizapi devices status --json
122
+ ```
123
+
124
+ Sample table columns include:
125
+
126
+ ```
127
+ name | status | device_category | device_sub_category | sleep | privacy | audio | ir_led | state_led | local_ip | local_rtsp_port | battery_level | alarm_schedules_enabled | alarm_notify | Motion_Trigger
128
+ ```
129
+
130
+ The CLI also computes a `switch_flags` map for each device (all switch states by name, e.g. `privacy`, `sleep`, `sound`, `infrared_light`, `light`, etc.).
131
+
132
+ ### camera
133
+
134
+ Requires `--serial`.
135
+
136
+ - Actions: `status`, `move`, `move_coords`, `unlock-door`, `unlock-gate`, `switch`, `alarm`, `select`
137
+ - Examples:
138
+
139
+ ```bash
140
+ # Camera status
141
+ pyezvizapi camera --serial ABC123 status
142
+
143
+ # PTZ move
144
+ pyezvizapi camera --serial ABC123 move --direction up --speed 5
145
+
146
+ # Move by coordinates
147
+ pyezvizapi camera --serial ABC123 move_coords --x 0.4 --y 0.6
148
+
149
+ # Switch setters
150
+ pyezvizapi camera --serial ABC123 switch --switch privacy --enable 1
151
+
152
+ # Alarm settings (push notify, sound level, do-not-disturb)
153
+ pyezvizapi camera --serial ABC123 alarm --notify 1 --sound 2 --do_not_disturb 0
154
+
155
+ # Battery camera work mode
156
+ pyezvizapi camera --serial ABC123 select --battery_work_mode POWER_SAVE
157
+ ```
158
+
159
+ ### devices_light
160
+
161
+ - Actions: `status`
162
+ - Example:
163
+
164
+ ```bash
165
+ pyezvizapi devices_light status
166
+ ```
167
+
168
+ ### home_defence_mode
169
+
170
+ Set global defence mode for the account/home.
171
+
172
+ ```bash
173
+ pyezvizapi home_defence_mode --mode HOME_MODE
174
+ ```
175
+
176
+ ### mqtt
177
+
178
+ Connect to Ezviz MQTT push notifications using the current session token. Use `--debug` to see connection details.
179
+
180
+ ```bash
181
+ pyezvizapi mqtt
182
+ ```
183
+
184
+ #### MQTT push test script (standalone)
185
+
186
+ For quick experimentation, a small helper script is included which can use a saved token file or prompt for credentials with MFA and save the session token:
187
+
188
+ ```bash
189
+ # With a previously saved token file
190
+ python config/custom_components/ezviz_cloud/pyezvizapi/test_mqtt.py --token-file ezviz_token.json
191
+
192
+ # Interactive login, then save token for next time
193
+ python config/custom_components/ezviz_cloud/pyezvizapi/test_mqtt.py --save-token
194
+
195
+ # Explicit credentials (not recommended for shared terminals)
196
+ python config/custom_components/ezviz_cloud/pyezvizapi/test_mqtt.py -u USER -p PASS --save-token
197
+ ```
198
+
199
+ ### pagelist
200
+
201
+ Dump the complete raw pagelist JSON. Great for exploring unknown fields in an editor (e.g. Notepad++).
202
+
203
+ ```bash
204
+ pyezvizapi pagelist > pagelist.json
205
+ ```
206
+
207
+ ### device_infos
208
+
209
+ Dump the processed device infos mapping (what the integration consumes). Optionally filter to one serial:
210
+
211
+ ```bash
212
+ # All devices
213
+ pyezvizapi device_infos > device_infos.json
214
+
215
+ # Single device
216
+ pyezvizapi device_infos --serial ABC123 > ABC123.json
217
+ ```
218
+
219
+ ## Remote door and gate unlock (CS-HPD7)
220
+
221
+ ```bash
222
+ pyezvizapi camera --serial BAXXXXXXX-BAYYYYYYY unlock-door
223
+ pyezvizapi camera --serial BAXXXXXXX-BAYYYYYYY unlock-gate
224
+ ```
225
+
226
+ ## RTSP authentication test (Basic → Digest)
227
+
228
+ Validate RTSP credentials by issuing a DESCRIBE request. Falls back from Basic to Digest auth automatically.
229
+
230
+ ```bash
231
+ python -c "from config.custom_components.ezviz_cloud.pyezvizapi.test_cam_rtsp import TestRTSPAuth as T; T('<IP>', '<USER>', '<PASS>', '/Streaming/Channels/101').main()"
232
+ ```
233
+
234
+ On success, the script prints a confirmation. On failure it raises one of:
235
+
236
+ - `InvalidHost`: Hostname/IP or port issue
237
+ - `AuthTestResultFailed`: Invalid credentials
238
+
239
+ ## Development
240
+
241
+ Please format with Ruff and check typing with mypy.
242
+
243
+ ```bash
244
+ ruff check .
245
+ mypy config/custom_components/ezviz_cloud/pyezvizapi
246
+ ```
247
+
248
+ Run style fixes where possible:
249
+
250
+ ```bash
251
+ ruff check --fix config/custom_components/ezviz_cloud/pyezvizapi
252
+ ```
253
+
254
+ Run tests with tox:
255
+
256
+ ```bash
257
+ tox
258
+ ```
259
+
260
+ ## Side Notes
261
+
262
+ There is no official API documentation. Much of this is based on reverse-engineering the Ezviz mobile app (Android/iOS). Some regions operate on separate endpoints; US example: `apiius.ezvizlife.com`.
263
+
264
+ Example:
265
+
266
+ ```bash
267
+ pyezvizapi -u username@domain.com -p PASS@123 -r apius.ezvizlife.com devices status
268
+ ```
269
+
270
+ For advanced troubleshooting or new feature research, MITM proxy tools like mitmproxy/Charles/Fiddler can be used to inspect traffic from the app (see community guides for SSL unpinning and WSA usage).
271
+
272
+ ## Contributing
273
+
274
+ Contributions are welcome — the API surface is large and there are many improvements possible.
275
+
276
+ ## Versioning
277
+
278
+ We follow SemVer when publishing the library. See repository tags for released versions.
279
+
280
+ ## License
281
+
282
+ Apache 2.0 — see `LICENSE.md`.
283
+
284
+ ---
285
+
286
+ Draft versions: 0.0.x
@@ -0,0 +1,21 @@
1
+ pyezvizapi/__init__.py,sha256=-OxHqxA9h0JZQW__GoZd1aByGquIHawCnNdeNlIg2Qo,3382
2
+ pyezvizapi/__main__.py,sha256=GmV-9mfXhbzZq3g--cpM6Tex8EX4DBHXE1jeDhD8UXs,24112
3
+ pyezvizapi/api_endpoints.py,sha256=NSUKafpmd_DaNtmlOsRplXeQB1PYjZ4roClt_BNnNyE,5802
4
+ pyezvizapi/camera.py,sha256=yHP4ENva6ep2BY85VvlyHvimCOCrHCwOCnCfpAC2mNY,28517
5
+ pyezvizapi/cas.py,sha256=3zHe-_a0KchCmGeAj1of-pV6oMPRUmSCIiDqBFsTK8A,6025
6
+ pyezvizapi/client.py,sha256=4c93Eg-0AQvxjxgBMX4dhi3ywOftxocr-4iIoM_HxRM,153968
7
+ pyezvizapi/constants.py,sha256=Ae838wTtHpOyNBnLQf56CFGHXpG9kPmgotjP9--J5V4,13570
8
+ pyezvizapi/exceptions.py,sha256=8rmxEUQdrziqMe-M1SeeRd0HtP2IDQ2xpJVj7wvOQyo,976
9
+ pyezvizapi/feature.py,sha256=8QeAo6vnIev_ovlDnVvnRbC39Xe6piLgBlFEuFE7qIA,16789
10
+ pyezvizapi/light_bulb.py,sha256=7kuOJmKsmAmE6KGJaUScjrRSTic8IhuToYrMRM-Y76s,7795
11
+ pyezvizapi/models.py,sha256=NQzwTP0yEe2IWU-Vc6nAn87xulpTuo0MX2Rcf0WxifA,4176
12
+ pyezvizapi/mqtt.py,sha256=uzEFT69LLCtP4paTSdCgMBkGAkuqRD02scfgqtK1bxU,22656
13
+ pyezvizapi/test_cam_rtsp.py,sha256=pbuanoKs_Pryt2f5QctHIngzJG1nD6kv8nulQYh2yPc,5162
14
+ pyezvizapi/test_mqtt.py,sha256=xUtyw5I3KBkMVGe2xhok7SWXFyZZMXrlMADoPo_5l_k,5744
15
+ pyezvizapi/utils.py,sha256=B-Lt4p14TTcJyrZvJ699GiUGj6qcd4vGmkKUf13bf_I,15455
16
+ pyezvizapi-1.0.4.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
17
+ pyezvizapi-1.0.4.3.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
+ pyezvizapi-1.0.4.3.dist-info/METADATA,sha256=WWMQuriHh98Vj2f3uTsQhFdlC0PdHrp8l_zmP7eqcaQ,7586
19
+ pyezvizapi-1.0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ pyezvizapi-1.0.4.3.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
21
+ pyezvizapi-1.0.4.3.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pyezvizapi
3
- Version: 1.0.3.3
4
- Summary: Pilot your Ezviz cameras
5
- Home-page: https://github.com/RenierM26/pyEzvizApi/
6
- Author: Renier Moorcroft
7
- Author-email: RenierM26@users.github.com
8
- License: Apache Software License 2.0
9
- Requires-Python: >=3.11
10
- License-File: LICENSE
11
- License-File: LICENSE.md
12
- Requires-Dist: requests
13
- Requires-Dist: pandas
14
- Requires-Dist: paho-mqtt
15
- Requires-Dist: xmltodict
16
- Requires-Dist: pycryptodome
17
- Dynamic: author
18
- Dynamic: author-email
19
- Dynamic: description
20
- Dynamic: home-page
21
- Dynamic: license
22
- Dynamic: license-file
23
- Dynamic: requires-dist
24
- Dynamic: requires-python
25
- Dynamic: summary
26
-
27
- Pilot your Ezviz cameras with this module. Please view readme on github
@@ -1,22 +0,0 @@
1
- pyezvizapi/__init__.py,sha256=mp3XbdXy00TyittfvvGrQ2oAaDeoebLE6_QU0Sbuf8o,2668
2
- pyezvizapi/__main__.py,sha256=9uttTuOfO22tzyomJIV8ebFJ-G-YUNDYOadZ_0AgdNA,20925
3
- pyezvizapi/api_endpoints.py,sha256=2M5Vs4YB1VWZGcowT-4Fj2hhRNjFh976LT3jtRrqvrc,5754
4
- pyezvizapi/camera.py,sha256=Pl5oIEdrFcv1Hz5sQI1IyyJIDCMjOjQdtExgKzmLoK8,22102
5
- pyezvizapi/cas.py,sha256=3zHe-_a0KchCmGeAj1of-pV6oMPRUmSCIiDqBFsTK8A,6025
6
- pyezvizapi/client.py,sha256=rQ95oGgx16M69BWA8WTmmpyX9d4kHdxt6biVMrjDmOQ,140990
7
- pyezvizapi/constants.py,sha256=5AxJYfof6NvebBcFvPkoKI6xinpkwmCnaauUvhvBMDY,12810
8
- pyezvizapi/exceptions.py,sha256=8rmxEUQdrziqMe-M1SeeRd0HtP2IDQ2xpJVj7wvOQyo,976
9
- pyezvizapi/feature.py,sha256=Sc-L-2qqz7GXmaWf0_j6QF84KRSw_emy1cWKhcs5HzE,7654
10
- pyezvizapi/light_bulb.py,sha256=9wgycG3dTvBbrsxQjQnXal-GA8VXPsIN1m-CTtRh8i0,7797
11
- pyezvizapi/models.py,sha256=NQzwTP0yEe2IWU-Vc6nAn87xulpTuo0MX2Rcf0WxifA,4176
12
- pyezvizapi/mqtt.py,sha256=aOL-gexZgYvCCaNQ03M4vZan91d5p2Fl_qsFykn9NW4,22365
13
- pyezvizapi/test_cam_rtsp.py,sha256=O9NHh-vcNFfnzNw8jbuhM9a_5TWfNZIMXaJP7Lmkaj4,5162
14
- pyezvizapi/test_mqtt.py,sha256=Orn-fwZPJIE4G5KROMX0MRAkLwU6nLb9LUtXyb2ZCQs,4147
15
- pyezvizapi/utils.py,sha256=ozGncEyaIJJ8VYw8f-xfM2OBmqR8eNYLq728FFvbvr8,12757
16
- pyezvizapi-1.0.3.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
17
- pyezvizapi-1.0.3.3.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
- pyezvizapi-1.0.3.3.dist-info/METADATA,sha256=zSGxukJtv5F88ejmK6fPoG8kPwQXO-y9t-xseLDVMQU,695
19
- pyezvizapi-1.0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
- pyezvizapi-1.0.3.3.dist-info/entry_points.txt,sha256=_BSJ3eNb2H_AZkRdsv1s4mojqWn3N7m503ujvg1SudA,56
21
- pyezvizapi-1.0.3.3.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
22
- pyezvizapi-1.0.3.3.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- pyezvizapi = pyezvizapi.__main__:main