controlid-sdk 0.3.3__tar.gz → 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.
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/PKG-INFO +1 -2
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/pyproject.toml +2 -3
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid/__init__.py +3 -2
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid/client.py +15 -9
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid/models.py +2 -2
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid_sdk.egg-info/PKG-INFO +1 -2
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/tests/test_client.py +11 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/README.md +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/setup.cfg +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid/constants.py +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid/exceptions.py +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid_sdk.egg-info/SOURCES.txt +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid_sdk.egg-info/dependency_links.txt +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid_sdk.egg-info/requires.txt +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/src/controlid_sdk.egg-info/top_level.txt +0 -0
- {controlid_sdk-0.3.3 → controlid_sdk-0.3.4}/tests/test_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlid-sdk
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A production-ready asynchronous Python SDK for ControlID access control devices, supporting biometrics, networking, and complex time schedules.
|
|
5
5
|
Author-email: Tulio Amancio <root@tsuriu.com.br>
|
|
6
6
|
License: MIT
|
|
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
15
|
Classifier: Operating System :: OS Independent
|
|
17
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
17
|
Classifier: Topic :: System :: Hardware
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "controlid-sdk"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.4"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Tulio Amancio", email="root@tsuriu.com.br" },
|
|
10
10
|
]
|
|
11
11
|
description = "A production-ready asynchronous Python SDK for ControlID access control devices, supporting biometrics, networking, and complex time schedules."
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
license = {
|
|
13
|
+
license = {text = "MIT"}
|
|
14
14
|
requires-python = ">=3.9"
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 4 - Beta",
|
|
@@ -20,7 +20,6 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.10",
|
|
21
21
|
"Programming Language :: Python :: 3.11",
|
|
22
22
|
"Programming Language :: Python :: 3.12",
|
|
23
|
-
"License :: OSI Approved :: MIT License",
|
|
24
23
|
"Operating System :: OS Independent",
|
|
25
24
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
25
|
"Topic :: System :: Hardware",
|
|
@@ -11,7 +11,6 @@ from .models import (
|
|
|
11
11
|
Door,
|
|
12
12
|
AccessLog,
|
|
13
13
|
Device,
|
|
14
|
-
TimeZone,
|
|
15
14
|
TimeSpan,
|
|
16
15
|
FaceTemplate,
|
|
17
16
|
CatraInfo,
|
|
@@ -32,7 +31,7 @@ from .exceptions import (
|
|
|
32
31
|
)
|
|
33
32
|
from . import constants
|
|
34
33
|
|
|
35
|
-
__version__ = "0.3.
|
|
34
|
+
__version__ = "0.3.4"
|
|
36
35
|
|
|
37
36
|
__all__ = [
|
|
38
37
|
"ControlIDClient",
|
|
@@ -44,6 +43,7 @@ __all__ = [
|
|
|
44
43
|
"UserGroup",
|
|
45
44
|
"AccessRule",
|
|
46
45
|
"TimeZone",
|
|
46
|
+
"TimeSpan",
|
|
47
47
|
"Area",
|
|
48
48
|
"Door",
|
|
49
49
|
"AccessLog",
|
|
@@ -67,3 +67,4 @@ __all__ = [
|
|
|
67
67
|
"constants",
|
|
68
68
|
]
|
|
69
69
|
|
|
70
|
+
|
|
@@ -2,10 +2,11 @@ import httpx
|
|
|
2
2
|
import asyncio
|
|
3
3
|
import time as _time
|
|
4
4
|
from typing import List, Dict, Any, Optional, Union
|
|
5
|
-
from urllib.parse import quote
|
|
5
|
+
from urllib.parse import quote
|
|
6
6
|
from . import constants as const
|
|
7
7
|
from . import exceptions as ex
|
|
8
|
-
from .models import User, Card,
|
|
8
|
+
from .models import User, Card, AccessLog, UserRole, CustomField, TimeZone, TimeSpan
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def _normalize_host(host: str) -> str:
|
|
@@ -107,14 +108,12 @@ class ControlIDClient:
|
|
|
107
108
|
payload = {"login": self.user, "password": self.password}
|
|
108
109
|
|
|
109
110
|
try:
|
|
110
|
-
logger_ctx = {"host": try_host, "user": self.user}
|
|
111
111
|
response = await self._client.post(url, json=payload, timeout=5.0) # Short timeout for login attempt
|
|
112
112
|
response.raise_for_status()
|
|
113
113
|
data = response.json()
|
|
114
114
|
if "session" in data:
|
|
115
115
|
self.session = data["session"]
|
|
116
116
|
if self.host != try_host:
|
|
117
|
-
old_host = self.host
|
|
118
117
|
self.host = try_host # Update to working host
|
|
119
118
|
if self.on_host_change:
|
|
120
119
|
try:
|
|
@@ -122,14 +121,16 @@ class ControlIDClient:
|
|
|
122
121
|
asyncio.create_task(self.on_host_change(self.host))
|
|
123
122
|
else:
|
|
124
123
|
self.on_host_change(self.host)
|
|
125
|
-
except Exception:
|
|
124
|
+
except Exception:
|
|
125
|
+
pass
|
|
126
126
|
return self.session
|
|
127
127
|
raise ex.AuthenticationError(f"Login failed: {data}")
|
|
128
128
|
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.ReadTimeout, httpx.WriteTimeout, httpx.PoolTimeout, httpx.ProtocolError, httpx.RemoteProtocolError, httpx.ProxyError) as e:
|
|
129
129
|
last_err = f"{type(e).__name__} on {proto}"
|
|
130
130
|
continue
|
|
131
131
|
except Exception as e:
|
|
132
|
-
if isinstance(e, ex.AuthenticationError):
|
|
132
|
+
if isinstance(e, ex.AuthenticationError):
|
|
133
|
+
raise
|
|
133
134
|
last_err = f"{type(e).__name__}: {str(e)}"
|
|
134
135
|
continue
|
|
135
136
|
|
|
@@ -228,7 +229,8 @@ class ControlIDClient:
|
|
|
228
229
|
except (httpx.ConnectError, httpx.ConnectTimeout, httpx.ReadTimeout, httpx.WriteTimeout, httpx.PoolTimeout) as e:
|
|
229
230
|
raise ex.DeviceUnreachableError(f"Device unreachable: {type(e).__name__} {e}")
|
|
230
231
|
except Exception as e:
|
|
231
|
-
if isinstance(e, (ex.APIError, ex.AuthenticationError, ex.DeviceUnreachableError)):
|
|
232
|
+
if isinstance(e, (ex.APIError, ex.AuthenticationError, ex.DeviceUnreachableError)):
|
|
233
|
+
raise
|
|
232
234
|
raise ex.ControlIDError(f"Request failed: {type(e).__name__} {e}")
|
|
233
235
|
|
|
234
236
|
async def get_system_information(self) -> Dict[str, Any]:
|
|
@@ -370,8 +372,10 @@ class ControlIDClient:
|
|
|
370
372
|
async def get_cards(self, user_id: Optional[int] = None, value: Optional[int] = None) -> List[Card]:
|
|
371
373
|
"""Return credentials for all users, or filtered by user_id or card value."""
|
|
372
374
|
where = {}
|
|
373
|
-
if user_id:
|
|
374
|
-
|
|
375
|
+
if user_id:
|
|
376
|
+
where["user_id"] = user_id
|
|
377
|
+
if value:
|
|
378
|
+
where["value"] = value
|
|
375
379
|
return [Card(**c) for c in await self.load_objects(const.TABLE_CARDS, where=where or None)]
|
|
376
380
|
|
|
377
381
|
async def add_card(self, card: Card) -> int:
|
|
@@ -767,6 +771,8 @@ class ControlIDClient:
|
|
|
767
771
|
|
|
768
772
|
await client.set_configuration({"face_id": {"qrcode_legacy_mode_enabled": "1"}})
|
|
769
773
|
"""
|
|
774
|
+
return await self.request(const.SET_CONFIGURATION, config)
|
|
775
|
+
|
|
770
776
|
async def export_audit_logs(self, config: int = 1, api: int = 1, usb: int = 0, network: int = 0, time: int = 1, online: int = 0, menu: int = 1) -> List[Dict[str, Any]]:
|
|
771
777
|
"""
|
|
772
778
|
Export device audit logs (system events, config changes, etc.).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: controlid-sdk
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A production-ready asynchronous Python SDK for ControlID access control devices, supporting biometrics, networking, and complex time schedules.
|
|
5
5
|
Author-email: Tulio Amancio <root@tsuriu.com.br>
|
|
6
6
|
License: MIT
|
|
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
16
15
|
Classifier: Operating System :: OS Independent
|
|
17
16
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
17
|
Classifier: Topic :: System :: Hardware
|
|
@@ -96,3 +96,14 @@ async def test_client_request_device_unreachable():
|
|
|
96
96
|
with patch.object(client._client, "post", new_callable=AsyncMock, side_effect=httpx.ConnectError("Connection refused")):
|
|
97
97
|
with pytest.raises(DeviceUnreachableError):
|
|
98
98
|
await client.request("/some-endpoint")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@pytest.mark.asyncio
|
|
102
|
+
async def test_set_configuration_calls_hardware_endpoint():
|
|
103
|
+
client = ControlIDClient(host="https://192.168.0.100")
|
|
104
|
+
payload = {"snmp_agent": {"snmp_enabled": "1"}}
|
|
105
|
+
with patch.object(client, "request", new_callable=AsyncMock, return_value={"status": "ok"}) as request:
|
|
106
|
+
result = await client.set_configuration(payload)
|
|
107
|
+
|
|
108
|
+
request.assert_awaited_once_with("/set_configuration.fcgi", payload)
|
|
109
|
+
assert result == {"status": "ok"}
|
|
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
|