pyezvizapi 1.0.1.6__py3-none-any.whl → 1.0.1.8__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.
- pyezvizapi/__init__.py +15 -2
- pyezvizapi/__main__.py +406 -283
- pyezvizapi/camera.py +488 -118
- pyezvizapi/cas.py +36 -43
- pyezvizapi/client.py +798 -1342
- pyezvizapi/constants.py +9 -2
- pyezvizapi/exceptions.py +9 -9
- pyezvizapi/light_bulb.py +80 -31
- pyezvizapi/models.py +103 -0
- pyezvizapi/mqtt.py +490 -133
- pyezvizapi/test_cam_rtsp.py +95 -109
- pyezvizapi/test_mqtt.py +135 -0
- pyezvizapi/utils.py +28 -2
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/METADATA +2 -2
- pyezvizapi-1.0.1.8.dist-info/RECORD +21 -0
- pyezvizapi-1.0.1.6.dist-info/RECORD +0 -19
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/WHEEL +0 -0
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/entry_points.txt +0 -0
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/licenses/LICENSE +0 -0
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/licenses/LICENSE.md +0 -0
- {pyezvizapi-1.0.1.6.dist-info → pyezvizapi-1.0.1.8.dist-info}/top_level.txt +0 -0
pyezvizapi/test_cam_rtsp.py
CHANGED
|
@@ -1,149 +1,135 @@
|
|
|
1
1
|
"""Test camera RTSP authentication."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
2
5
|
import base64
|
|
3
6
|
import hashlib
|
|
4
7
|
import socket
|
|
8
|
+
from typing import TypedDict
|
|
5
9
|
|
|
6
10
|
from .exceptions import AuthTestResultFailed, InvalidHost
|
|
7
11
|
|
|
8
12
|
|
|
9
|
-
def genmsg_describe(url, seq, user_agent, auth_seq):
|
|
10
|
-
"""Generate RTSP
|
|
11
|
-
msg_ret = "DESCRIBE
|
|
12
|
-
msg_ret += "CSeq:
|
|
13
|
-
msg_ret += "Authorization:
|
|
14
|
-
msg_ret += "User-Agent:
|
|
15
|
-
msg_ret += "Accept: application/sdp\r\n"
|
|
16
|
-
msg_ret += "\r\n"
|
|
13
|
+
def genmsg_describe(url: str, seq: int, user_agent: str, auth_seq: str) -> str:
|
|
14
|
+
"""Generate RTSP DESCRIBE request message."""
|
|
15
|
+
msg_ret = f"DESCRIBE {url} RTSP/1.0\r\n"
|
|
16
|
+
msg_ret += f"CSeq: {seq}\r\n"
|
|
17
|
+
msg_ret += f"Authorization: {auth_seq}\r\n"
|
|
18
|
+
msg_ret += f"User-Agent: {user_agent}\r\n"
|
|
19
|
+
msg_ret += "Accept: application/sdp\r\n\r\n"
|
|
17
20
|
return msg_ret
|
|
18
21
|
|
|
19
22
|
|
|
23
|
+
class RTSPDetails(TypedDict):
|
|
24
|
+
"""Typed structure for RTSP test parameters."""
|
|
25
|
+
bufLen: int
|
|
26
|
+
defaultServerIp: str
|
|
27
|
+
defaultServerPort: int
|
|
28
|
+
defaultTestUri: str
|
|
29
|
+
defaultUserAgent: str
|
|
30
|
+
defaultUsername: str | None
|
|
31
|
+
defaultPassword: str | None
|
|
32
|
+
|
|
33
|
+
|
|
20
34
|
class TestRTSPAuth:
|
|
21
|
-
"""Test RTSP credentials."""
|
|
35
|
+
"""Test RTSP credentials against an RTSP server."""
|
|
36
|
+
|
|
37
|
+
_rtsp_details: RTSPDetails
|
|
22
38
|
|
|
23
39
|
def __init__(
|
|
24
|
-
self,
|
|
25
|
-
ip_addr,
|
|
26
|
-
username=None,
|
|
27
|
-
password=None,
|
|
28
|
-
test_uri="",
|
|
40
|
+
self, ip_addr: str, username: str | None = None, password: str | None = None, test_uri: str = ""
|
|
29
41
|
) -> None:
|
|
30
42
|
"""Initialize RTSP credential test."""
|
|
31
|
-
self._rtsp_details =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def generate_auth_string(self, realm, method, uri, nonce):
|
|
42
|
-
"""Generate
|
|
43
|
-
map_return_info = {}
|
|
43
|
+
self._rtsp_details = RTSPDetails(
|
|
44
|
+
bufLen=1024,
|
|
45
|
+
defaultServerIp=ip_addr,
|
|
46
|
+
defaultServerPort=554,
|
|
47
|
+
defaultTestUri=test_uri,
|
|
48
|
+
defaultUserAgent="RTSP Client",
|
|
49
|
+
defaultUsername=username,
|
|
50
|
+
defaultPassword=password,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def generate_auth_string(self, realm: bytes, method: str, uri: str, nonce: bytes) -> str:
|
|
54
|
+
"""Generate the HTTP Digest Authorization header value."""
|
|
44
55
|
m_1 = hashlib.md5(
|
|
45
|
-
f"{self._rtsp_details['defaultUsername']}:"
|
|
46
|
-
f"{realm.decode()}:"
|
|
47
|
-
f"{self._rtsp_details['defaultPassword']}".encode()
|
|
56
|
+
f"{self._rtsp_details['defaultUsername']}:{realm.decode()}:{self._rtsp_details['defaultPassword']}".encode()
|
|
48
57
|
).hexdigest()
|
|
49
58
|
m_2 = hashlib.md5(f"{method}:{uri}".encode()).hexdigest()
|
|
50
|
-
response = hashlib.md5(f"{m_1}:{nonce}:{m_2}".encode()).hexdigest()
|
|
59
|
+
response = hashlib.md5(f"{m_1}:{nonce.decode()}:{m_2}".encode()).hexdigest()
|
|
51
60
|
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
return (
|
|
62
|
+
"Digest "
|
|
54
63
|
f"username=\"{self._rtsp_details['defaultUsername']}\", "
|
|
55
|
-
f
|
|
56
|
-
|
|
57
|
-
f
|
|
58
|
-
f
|
|
59
|
-
f
|
|
64
|
+
f"realm=\"{realm.decode()}\", "
|
|
65
|
+
'algorithm="MD5", '
|
|
66
|
+
f"nonce=\"{nonce.decode()}\", "
|
|
67
|
+
f"uri=\"{uri}\", "
|
|
68
|
+
f"response=\"{response}\""
|
|
60
69
|
)
|
|
61
|
-
return map_return_info
|
|
62
70
|
|
|
63
|
-
def main(self):
|
|
64
|
-
"""
|
|
65
|
-
session = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
71
|
+
def main(self) -> None:
|
|
72
|
+
"""Open RTSP socket, try Basic and then Digest auth for DESCRIBE."""
|
|
73
|
+
session: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
66
74
|
|
|
67
75
|
try:
|
|
68
76
|
session.connect(
|
|
69
|
-
(
|
|
70
|
-
self._rtsp_details["defaultServerIp"],
|
|
71
|
-
self._rtsp_details["defaultServerPort"],
|
|
72
|
-
)
|
|
77
|
+
(self._rtsp_details["defaultServerIp"], self._rtsp_details["defaultServerPort"])
|
|
73
78
|
)
|
|
74
|
-
|
|
75
79
|
except TimeoutError as err:
|
|
76
80
|
raise AuthTestResultFailed("Invalid ip or camera hibernating") from err
|
|
77
|
-
|
|
78
81
|
except (socket.gaierror, ConnectionRefusedError) as err:
|
|
79
82
|
raise InvalidHost("Invalid IP or Hostname") from err
|
|
80
83
|
|
|
81
|
-
seq = 1
|
|
84
|
+
seq: int = 1
|
|
82
85
|
|
|
83
|
-
url =
|
|
84
|
-
"rtsp://"
|
|
85
|
-
+ self._rtsp_details["defaultServerIp"]
|
|
86
|
-
+ self._rtsp_details["defaultTestUri"]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
auth_seq = base64.b64encode(
|
|
90
|
-
f"{self._rtsp_details['defaultUsername']}:"
|
|
91
|
-
f"{self._rtsp_details['defaultPassword']}".encode("ascii")
|
|
92
|
-
)
|
|
93
|
-
auth_seq = "Basic " + auth_seq.decode()
|
|
86
|
+
url: str = "rtsp://" + self._rtsp_details["defaultServerIp"] + self._rtsp_details["defaultTestUri"]
|
|
94
87
|
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
# Basic Authorization header
|
|
89
|
+
auth_b64: bytes = base64.b64encode(
|
|
90
|
+
f"{self._rtsp_details['defaultUsername']}:{self._rtsp_details['defaultPassword']}".encode("ascii")
|
|
97
91
|
)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
msg1 = session.recv(self._rtsp_details["bufLen"])
|
|
104
|
-
seq
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
print(
|
|
130
|
-
genmsg_describe(
|
|
131
|
-
url, seq, self._rtsp_details["defaultUserAgent"], auth_seq
|
|
132
|
-
)
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
session.send(
|
|
136
|
-
genmsg_describe(
|
|
137
|
-
url, seq, self._rtsp_details["defaultUserAgent"], auth_seq
|
|
138
|
-
).encode()
|
|
139
|
-
)
|
|
92
|
+
auth_seq: str = "Basic " + auth_b64.decode()
|
|
93
|
+
|
|
94
|
+
describe = genmsg_describe(url, seq, self._rtsp_details["defaultUserAgent"], auth_seq)
|
|
95
|
+
print(describe)
|
|
96
|
+
session.send(describe.encode())
|
|
97
|
+
msg1: bytes = session.recv(self._rtsp_details["bufLen"])
|
|
98
|
+
seq += 1
|
|
99
|
+
|
|
100
|
+
decoded = msg1.decode()
|
|
101
|
+
if "200 OK" in decoded:
|
|
102
|
+
print(f"Basic auth result: {decoded}")
|
|
103
|
+
print("Basic Auth test passed. Credentials Valid!")
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
if "Unauthorized" in decoded:
|
|
107
|
+
# Basic failed, do new DESCRIBE with digest authentication.
|
|
108
|
+
start = decoded.find("realm")
|
|
109
|
+
begin = decoded.find('"', start)
|
|
110
|
+
end = decoded.find('"', begin + 1)
|
|
111
|
+
realm: bytes = msg1[begin + 1 : end]
|
|
112
|
+
|
|
113
|
+
start = decoded.find("nonce")
|
|
114
|
+
begin = decoded.find('"', start)
|
|
115
|
+
end = decoded.find('"', begin + 1)
|
|
116
|
+
nonce: bytes = msg1[begin + 1 : end]
|
|
117
|
+
|
|
118
|
+
auth_seq = self.generate_auth_string(realm, "DESCRIBE", self._rtsp_details["defaultTestUri"], nonce)
|
|
119
|
+
|
|
120
|
+
describe = genmsg_describe(url, seq, self._rtsp_details["defaultUserAgent"], auth_seq)
|
|
121
|
+
print(describe)
|
|
122
|
+
session.send(describe.encode())
|
|
140
123
|
msg1 = session.recv(self._rtsp_details["bufLen"])
|
|
141
|
-
|
|
124
|
+
decoded = msg1.decode()
|
|
125
|
+
print(f"Digest auth result: {decoded}")
|
|
142
126
|
|
|
143
|
-
if
|
|
144
|
-
|
|
127
|
+
if "200 OK" in decoded:
|
|
128
|
+
print("Digest Auth test Passed. Credentials Valid!")
|
|
129
|
+
return
|
|
145
130
|
|
|
146
|
-
if
|
|
131
|
+
if "401 Unauthorized" in decoded:
|
|
147
132
|
raise AuthTestResultFailed("Credentials not valid!!")
|
|
148
133
|
|
|
149
|
-
|
|
134
|
+
print("Basic Auth test passed. Credentials Valid!")
|
|
135
|
+
# ruff: noqa: T201
|
pyezvizapi/test_mqtt.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""MQTT test module.
|
|
2
|
+
|
|
3
|
+
Run a simple MQTT listener using either a saved token file
|
|
4
|
+
(`--token-file ezviz_token.json`) or by prompting for username/password
|
|
5
|
+
with MFA similar to the main CLI.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
from getpass import getpass
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
import sys
|
|
16
|
+
import time
|
|
17
|
+
from typing import Any, cast
|
|
18
|
+
|
|
19
|
+
from .client import EzvizClient
|
|
20
|
+
from .exceptions import EzvizAuthVerificationCode, PyEzvizError
|
|
21
|
+
|
|
22
|
+
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
|
|
23
|
+
|
|
24
|
+
LOG_FILE = Path("mqtt_messages.jsonl") # JSON Lines format
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def message_handler(msg: dict[str, Any]) -> None:
|
|
28
|
+
"""Handle new MQTT messages by printing and saving them to a file."""
|
|
29
|
+
print("📩 New MQTT message:", msg)
|
|
30
|
+
with LOG_FILE.open("a", encoding="utf-8") as f:
|
|
31
|
+
f.write(json.dumps(msg, ensure_ascii=False) + "\n")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _load_token_file(path: str | None) -> dict[str, Any] | None:
|
|
35
|
+
if not path:
|
|
36
|
+
return None
|
|
37
|
+
p = Path(path)
|
|
38
|
+
if not p.exists():
|
|
39
|
+
return None
|
|
40
|
+
try:
|
|
41
|
+
return cast(dict[str, Any], json.loads(p.read_text(encoding="utf-8")))
|
|
42
|
+
except (OSError, json.JSONDecodeError):
|
|
43
|
+
logging.getLogger(__name__).warning("Failed to read token file: %s", p)
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _save_token_file(path: str | None, token: dict[str, Any]) -> None:
|
|
48
|
+
if not path:
|
|
49
|
+
return
|
|
50
|
+
p = Path(path)
|
|
51
|
+
try:
|
|
52
|
+
p.write_text(json.dumps(token, indent=2), encoding="utf-8")
|
|
53
|
+
logging.getLogger(__name__).info("Saved token to %s", p)
|
|
54
|
+
except OSError:
|
|
55
|
+
logging.getLogger(__name__).warning("Failed to save token file: %s", p)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def main(argv: list[str] | None = None) -> int:
|
|
59
|
+
"""Entry point for testing MQTT messages."""
|
|
60
|
+
parser = argparse.ArgumentParser(prog="test_mqtt")
|
|
61
|
+
parser.add_argument("-u", "--username", required=False, help="Ezviz username")
|
|
62
|
+
parser.add_argument("-p", "--password", required=False, help="Ezviz password")
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"-r",
|
|
65
|
+
"--region",
|
|
66
|
+
required=False,
|
|
67
|
+
default="apiieu.ezvizlife.com",
|
|
68
|
+
help="Ezviz API region",
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument(
|
|
71
|
+
"--token-file",
|
|
72
|
+
type=str,
|
|
73
|
+
default="ezviz_token.json",
|
|
74
|
+
help="Path to JSON token file (default: ezviz_token.json)",
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--save-token",
|
|
78
|
+
action="store_true",
|
|
79
|
+
help="Save token to --token-file after successful login",
|
|
80
|
+
)
|
|
81
|
+
args = parser.parse_args(argv)
|
|
82
|
+
|
|
83
|
+
token = _load_token_file(args.token_file)
|
|
84
|
+
|
|
85
|
+
username = args.username
|
|
86
|
+
password = args.password
|
|
87
|
+
|
|
88
|
+
# If no token and missing username/password, prompt interactively
|
|
89
|
+
if not token and (not username or not password):
|
|
90
|
+
print("No token found. Please enter Ezviz credentials.")
|
|
91
|
+
if not username:
|
|
92
|
+
username = input("Username: ")
|
|
93
|
+
if not password:
|
|
94
|
+
password = getpass("Password: ")
|
|
95
|
+
|
|
96
|
+
client = EzvizClient(username, password, args.region, token=token)
|
|
97
|
+
|
|
98
|
+
# Login if we have credentials (to refresh session and populate service URLs)
|
|
99
|
+
if username and password:
|
|
100
|
+
try:
|
|
101
|
+
client.login()
|
|
102
|
+
except EzvizAuthVerificationCode:
|
|
103
|
+
mfa_code = input("MFA code required, please input MFA code.\n")
|
|
104
|
+
try:
|
|
105
|
+
code_int = int(mfa_code.strip())
|
|
106
|
+
except ValueError:
|
|
107
|
+
code_int = None
|
|
108
|
+
client.login(sms_code=code_int)
|
|
109
|
+
except PyEzvizError as exp:
|
|
110
|
+
print(f"Login failed: {exp}")
|
|
111
|
+
return 1
|
|
112
|
+
|
|
113
|
+
# Start MQTT client
|
|
114
|
+
mqtt_client = client.get_mqtt_client(on_message_callback=message_handler)
|
|
115
|
+
mqtt_client.connect()
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
print("Listening for MQTT messages... (Ctrl+C to quit)")
|
|
119
|
+
while True:
|
|
120
|
+
time.sleep(1)
|
|
121
|
+
except KeyboardInterrupt:
|
|
122
|
+
print("\nStopping...")
|
|
123
|
+
finally:
|
|
124
|
+
mqtt_client.stop()
|
|
125
|
+
print("Stopped.")
|
|
126
|
+
|
|
127
|
+
if args.save_token and args.token_file:
|
|
128
|
+
_save_token_file(args.token_file, cast(dict[str, Any], client._token)) # noqa: SLF001
|
|
129
|
+
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
sys.exit(main())
|
|
135
|
+
# ruff: noqa: T201
|
pyezvizapi/utils.py
CHANGED
|
@@ -6,6 +6,7 @@ from hashlib import md5
|
|
|
6
6
|
import json
|
|
7
7
|
import logging
|
|
8
8
|
from typing import Any
|
|
9
|
+
import uuid
|
|
9
10
|
|
|
10
11
|
from Crypto.Cipher import AES
|
|
11
12
|
|
|
@@ -61,10 +62,12 @@ def fetch_nested_value(data: Any, keys: list, default_value: Any = None) -> Any:
|
|
|
61
62
|
try:
|
|
62
63
|
for key in keys:
|
|
63
64
|
data = data[key]
|
|
64
|
-
|
|
65
|
+
|
|
65
66
|
except (KeyError, TypeError):
|
|
66
67
|
return default_value
|
|
67
68
|
|
|
69
|
+
return data
|
|
70
|
+
|
|
68
71
|
|
|
69
72
|
def decrypt_image(input_data: bytes, password: str) -> bytes:
|
|
70
73
|
"""Decrypts image data with provided password.
|
|
@@ -80,7 +83,6 @@ def decrypt_image(input_data: bytes, password: str) -> bytes:
|
|
|
80
83
|
bytes: Decrypted image data
|
|
81
84
|
|
|
82
85
|
"""
|
|
83
|
-
|
|
84
86
|
if len(input_data) < 48:
|
|
85
87
|
raise PyEzvizError("Invalid image data")
|
|
86
88
|
|
|
@@ -162,3 +164,27 @@ def deep_merge(dict1: Any, dict2: Any) -> Any:
|
|
|
162
164
|
merged[key] = dict2[key]
|
|
163
165
|
|
|
164
166
|
return merged
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def generate_unique_code() -> str:
|
|
170
|
+
"""Generate a deterministic, platform-agnostic unique code for the current host.
|
|
171
|
+
|
|
172
|
+
This function retrieves the host's MAC address using Python's standard
|
|
173
|
+
`uuid.getnode()` (works on Windows, Linux, macOS), converts it to a
|
|
174
|
+
canonical string representation, and then hashes it using MD5 to produce
|
|
175
|
+
a fixed-length hexadecimal string.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
str: A 32-character hexadecimal string uniquely representing
|
|
179
|
+
the host's MAC address. For example:
|
|
180
|
+
'a94e6756hghjgfghg49e0f310d9e44a'.
|
|
181
|
+
|
|
182
|
+
Notes:
|
|
183
|
+
- The output is deterministic: the same machine returns the same code.
|
|
184
|
+
- If the MAC address changes (e.g., different network adapter),
|
|
185
|
+
the output will change.
|
|
186
|
+
- MD5 is used here only for ID generation, not for security.
|
|
187
|
+
"""
|
|
188
|
+
mac_int = uuid.getnode()
|
|
189
|
+
mac_str = ":".join(f"{(mac_int >> i) & 0xFF:02x}" for i in range(40, -1, -8))
|
|
190
|
+
return md5(mac_str.encode("utf-8")).hexdigest()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyezvizapi
|
|
3
|
-
Version: 1.0.1.
|
|
3
|
+
Version: 1.0.1.8
|
|
4
4
|
Summary: Pilot your Ezviz cameras
|
|
5
5
|
Home-page: https://github.com/RenierM26/pyEzvizApi/
|
|
6
6
|
Author: Renier Moorcroft
|
|
7
7
|
Author-email: RenierM26@users.github.com
|
|
8
8
|
License: Apache Software License 2.0
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
License-File: LICENSE.md
|
|
12
12
|
Requires-Dist: requests
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
pyezvizapi/__init__.py,sha256=IDnIN_nfIISVwuy0cVBh4wspgAav6MuOJCQGajjyU3g,1881
|
|
2
|
+
pyezvizapi/__main__.py,sha256=SeV954H-AV-U1thNxRd7rWTGtSlfWyNzdrjF8gikYus,20777
|
|
3
|
+
pyezvizapi/api_endpoints.py,sha256=rk6VinLVCn-B6DxnhfV79liplNpgUsipNbTEa_MRVwU,2755
|
|
4
|
+
pyezvizapi/camera.py,sha256=HcJPUXQnYEAVqh6alphuRiMl6al28C9JORP6LO3m_es,24927
|
|
5
|
+
pyezvizapi/cas.py,sha256=ISmb-eTPuacI10L87lULbQ-oDMBuygO7Tf-s9f-tvYE,5995
|
|
6
|
+
pyezvizapi/client.py,sha256=dcHAsdtfSTkVCrkaEZKpFggDXX4t5debpuxc76sOy-k,71267
|
|
7
|
+
pyezvizapi/constants.py,sha256=R0zGg8Rv59An36dSXQLl3WU7VbpTbpo2comk9VJc68k,12535
|
|
8
|
+
pyezvizapi/exceptions.py,sha256=8rmxEUQdrziqMe-M1SeeRd0HtP2IDQ2xpJVj7wvOQyo,976
|
|
9
|
+
pyezvizapi/light_bulb.py,sha256=9wgycG3dTvBbrsxQjQnXal-GA8VXPsIN1m-CTtRh8i0,7797
|
|
10
|
+
pyezvizapi/models.py,sha256=NQzwTP0yEe2IWU-Vc6nAn87xulpTuo0MX2Rcf0WxifA,4176
|
|
11
|
+
pyezvizapi/mqtt.py,sha256=TxIjg2Xk6Qh4LWQQXKS6vpgLj2Rl2KOkDRwaz46PGUU,22367
|
|
12
|
+
pyezvizapi/test_cam_rtsp.py,sha256=WGSM5EiOTl_r1mWHoMb7bXHm_BCn1P9X_669YQ38r6k,4903
|
|
13
|
+
pyezvizapi/test_mqtt.py,sha256=Orn-fwZPJIE4G5KROMX0MRAkLwU6nLb9LUtXyb2ZCQs,4147
|
|
14
|
+
pyezvizapi/utils.py,sha256=o342o3LI9eP8qla1vXM2rqlVbdTiLK0dAqrkyUSXpg8,5939
|
|
15
|
+
pyezvizapi-1.0.1.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
+
pyezvizapi-1.0.1.8.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
17
|
+
pyezvizapi-1.0.1.8.dist-info/METADATA,sha256=7t6tdY_1RMQ524_vV-Z5zcMZr5hPeK7h8PIYXsrsUoA,695
|
|
18
|
+
pyezvizapi-1.0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
pyezvizapi-1.0.1.8.dist-info/entry_points.txt,sha256=_BSJ3eNb2H_AZkRdsv1s4mojqWn3N7m503ujvg1SudA,56
|
|
20
|
+
pyezvizapi-1.0.1.8.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
|
|
21
|
+
pyezvizapi-1.0.1.8.dist-info/RECORD,,
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
pyezvizapi/__init__.py,sha256=yWITfZOi5zvKblfaeB2ft-wXYmZ2NviYVq_tsK8YkSk,1334
|
|
2
|
-
pyezvizapi/__main__.py,sha256=OsawbYa-eoLUTOMr4xWeryq4sDzs09WL9GELKQTbda8,16396
|
|
3
|
-
pyezvizapi/api_endpoints.py,sha256=rk6VinLVCn-B6DxnhfV79liplNpgUsipNbTEa_MRVwU,2755
|
|
4
|
-
pyezvizapi/camera.py,sha256=BpmLySbnWWEKimSwgjx_cB60Q-6dbgdY-NA--NzUvps,11486
|
|
5
|
-
pyezvizapi/cas.py,sha256=d31ZflYSD9P40MnsNRNZbT0HVLvlnHokKpLbdAjWQ74,5631
|
|
6
|
-
pyezvizapi/client.py,sha256=lak8r1Src1R57dQYwzVDq7NRt1iIOdkESyuR2FWqmRQ,85563
|
|
7
|
-
pyezvizapi/constants.py,sha256=jjLO-Ne9jq9m9_giYB4rnPXDZKkzKhesVXBqP1B3-00,12304
|
|
8
|
-
pyezvizapi/exceptions.py,sha256=28lLyM0ILTRHgWqr9D-DqqKFXx7POuF0WAZctdC8Kbc,735
|
|
9
|
-
pyezvizapi/light_bulb.py,sha256=ADLrPZ6NL4vANzmohU63QuD9qVGkKHkX9C0o7Evbv-A,5730
|
|
10
|
-
pyezvizapi/mqtt.py,sha256=Y4X99Z0Avm32SE8vog7CNsv6tGUPmPYUZUgPDGS0QJA,7866
|
|
11
|
-
pyezvizapi/test_cam_rtsp.py,sha256=w7GPcYIeK78TxL8zFDihdGSDQNWcYrurwZOr6uFzzgo,4902
|
|
12
|
-
pyezvizapi/utils.py,sha256=5J10o3h-y8prWDvl3LSAF-9wS1jBgBMg5cpAEebcuSM,4936
|
|
13
|
-
pyezvizapi-1.0.1.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
14
|
-
pyezvizapi-1.0.1.6.dist-info/licenses/LICENSE.md,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
15
|
-
pyezvizapi-1.0.1.6.dist-info/METADATA,sha256=GAvczQGW5LSpRKQg5jCaExw9KM4GcvqKa0L9WYWvMxc,694
|
|
16
|
-
pyezvizapi-1.0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
-
pyezvizapi-1.0.1.6.dist-info/entry_points.txt,sha256=_BSJ3eNb2H_AZkRdsv1s4mojqWn3N7m503ujvg1SudA,56
|
|
18
|
-
pyezvizapi-1.0.1.6.dist-info/top_level.txt,sha256=gMZTelIi8z7pXyTCQLLaIkxVRrDQ_lS2NEv0WgfHrHs,11
|
|
19
|
-
pyezvizapi-1.0.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|