nuki-web-api 0.1.0__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.
- nuki_web_api-0.1.0/PKG-INFO +34 -0
- nuki_web_api-0.1.0/README.md +20 -0
- nuki_web_api-0.1.0/nuki_web_api.egg-info/PKG-INFO +34 -0
- nuki_web_api-0.1.0/nuki_web_api.egg-info/SOURCES.txt +23 -0
- nuki_web_api-0.1.0/nuki_web_api.egg-info/dependency_links.txt +1 -0
- nuki_web_api-0.1.0/nuki_web_api.egg-info/requires.txt +1 -0
- nuki_web_api-0.1.0/nuki_web_api.egg-info/top_level.txt +1 -0
- nuki_web_api-0.1.0/nukiwebapi/__init__.py +3 -0
- nuki_web_api-0.1.0/nukiwebapi/account.py +84 -0
- nuki_web_api-0.1.0/nukiwebapi/account_user.py +20 -0
- nuki_web_api-0.1.0/nukiwebapi/address.py +31 -0
- nuki_web_api-0.1.0/nukiwebapi/address_reservation.py +25 -0
- nuki_web_api-0.1.0/nukiwebapi/address_token.py +25 -0
- nuki_web_api-0.1.0/nukiwebapi/advanced_api.py +45 -0
- nuki_web_api-0.1.0/nukiwebapi/api_key.py +47 -0
- nuki_web_api-0.1.0/nukiwebapi/company.py +10 -0
- nuki_web_api-0.1.0/nukiwebapi/notification.py +26 -0
- nuki_web_api-0.1.0/nukiwebapi/nuki_web_api.py +78 -0
- nuki_web_api-0.1.0/nukiwebapi/opener.py +23 -0
- nuki_web_api-0.1.0/nukiwebapi/service.py +32 -0
- nuki_web_api-0.1.0/nukiwebapi/smartlock.py +166 -0
- nuki_web_api-0.1.0/nukiwebapi/smartlock_auth.py +58 -0
- nuki_web_api-0.1.0/nukiwebapi/smartlock_log.py +18 -0
- nuki_web_api-0.1.0/pyproject.toml +26 -0
- nuki_web_api-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nuki-web-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python wrapper for the Nuki Web API
|
|
5
|
+
Author-email: barghest89 <blachosf@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/barghest89/nuki-web-api
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/barghest89/nuki-web-api/issues
|
|
9
|
+
Keywords: nuki,api,smartlock
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: requests==2.32.5
|
|
14
|
+
|
|
15
|
+
# nukiwebapi
|
|
16
|
+
|
|
17
|
+
A Python wrapper for the [Nuki Web API](https://developer.nuki.io/).
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install nukiwebapi
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
```Python
|
|
27
|
+
from nukiwebapi import NukiWebAPI
|
|
28
|
+
|
|
29
|
+
client = NukiWebAPI("YOUR_ACCESS_TOKEN")
|
|
30
|
+
|
|
31
|
+
for lock_id, lock in client.smartlocks():
|
|
32
|
+
print(lock_id, lock.name)
|
|
33
|
+
lock.unlock()
|
|
34
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# nukiwebapi
|
|
2
|
+
|
|
3
|
+
A Python wrapper for the [Nuki Web API](https://developer.nuki.io/).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install nukiwebapi
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```Python
|
|
13
|
+
from nukiwebapi import NukiWebAPI
|
|
14
|
+
|
|
15
|
+
client = NukiWebAPI("YOUR_ACCESS_TOKEN")
|
|
16
|
+
|
|
17
|
+
for lock_id, lock in client.smartlocks():
|
|
18
|
+
print(lock_id, lock.name)
|
|
19
|
+
lock.unlock()
|
|
20
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nuki-web-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python wrapper for the Nuki Web API
|
|
5
|
+
Author-email: barghest89 <blachosf@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/barghest89/nuki-web-api
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/barghest89/nuki-web-api/issues
|
|
9
|
+
Keywords: nuki,api,smartlock
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: requests==2.32.5
|
|
14
|
+
|
|
15
|
+
# nukiwebapi
|
|
16
|
+
|
|
17
|
+
A Python wrapper for the [Nuki Web API](https://developer.nuki.io/).
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install nukiwebapi
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
```Python
|
|
27
|
+
from nukiwebapi import NukiWebAPI
|
|
28
|
+
|
|
29
|
+
client = NukiWebAPI("YOUR_ACCESS_TOKEN")
|
|
30
|
+
|
|
31
|
+
for lock_id, lock in client.smartlocks():
|
|
32
|
+
print(lock_id, lock.name)
|
|
33
|
+
lock.unlock()
|
|
34
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
nuki_web_api.egg-info/PKG-INFO
|
|
4
|
+
nuki_web_api.egg-info/SOURCES.txt
|
|
5
|
+
nuki_web_api.egg-info/dependency_links.txt
|
|
6
|
+
nuki_web_api.egg-info/requires.txt
|
|
7
|
+
nuki_web_api.egg-info/top_level.txt
|
|
8
|
+
nukiwebapi/__init__.py
|
|
9
|
+
nukiwebapi/account.py
|
|
10
|
+
nukiwebapi/account_user.py
|
|
11
|
+
nukiwebapi/address.py
|
|
12
|
+
nukiwebapi/address_reservation.py
|
|
13
|
+
nukiwebapi/address_token.py
|
|
14
|
+
nukiwebapi/advanced_api.py
|
|
15
|
+
nukiwebapi/api_key.py
|
|
16
|
+
nukiwebapi/company.py
|
|
17
|
+
nukiwebapi/notification.py
|
|
18
|
+
nukiwebapi/nuki_web_api.py
|
|
19
|
+
nukiwebapi/opener.py
|
|
20
|
+
nukiwebapi/service.py
|
|
21
|
+
nukiwebapi/smartlock.py
|
|
22
|
+
nukiwebapi/smartlock_auth.py
|
|
23
|
+
nukiwebapi/smartlock_log.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
requests==2.32.5
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nukiwebapi
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# ---------------------------
|
|
2
|
+
# Account sub-class
|
|
3
|
+
# ---------------------------
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
class Account:
|
|
7
|
+
def __init__(self, client):
|
|
8
|
+
self.client = client # reference to the parent NukiClient
|
|
9
|
+
|
|
10
|
+
# GET /account
|
|
11
|
+
def get(self) -> Dict[str, Any]:
|
|
12
|
+
return self.client._request("GET", "/account")
|
|
13
|
+
|
|
14
|
+
# POST /account
|
|
15
|
+
def update(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
16
|
+
return self.client._request("POST", "/account", json=data)
|
|
17
|
+
|
|
18
|
+
# DELETE /account
|
|
19
|
+
def delete(self) -> Dict[str, Any]:
|
|
20
|
+
return self.client._request("DELETE", "/account")
|
|
21
|
+
|
|
22
|
+
# POST /account/email/change
|
|
23
|
+
def change_email(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
24
|
+
return self.client._request("POST", "/account/email/change", json=data)
|
|
25
|
+
|
|
26
|
+
# POST /account/email/verify
|
|
27
|
+
def verify_email(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
28
|
+
return self.client._request("POST", "/account/email/verify", json=data)
|
|
29
|
+
|
|
30
|
+
# GET /account/integration
|
|
31
|
+
def list_integrations(self) -> Dict[str, Any]:
|
|
32
|
+
return self.client._request("GET", "/account/integration")
|
|
33
|
+
|
|
34
|
+
# DELETE /account/integration
|
|
35
|
+
def delete_integration(self, integration_id: str) -> Dict[str, Any]:
|
|
36
|
+
return self.client._request("DELETE", "/account/integration", json={"id": integration_id})
|
|
37
|
+
|
|
38
|
+
# POST /account/otp
|
|
39
|
+
def enable_otp(self) -> Dict[str, Any]:
|
|
40
|
+
return self.client._request("POST", "/account/otp")
|
|
41
|
+
|
|
42
|
+
# PUT /account/otp
|
|
43
|
+
def create_otp(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
44
|
+
return self.client._request("PUT", "/account/otp", json=data)
|
|
45
|
+
|
|
46
|
+
# DELETE /account/otp
|
|
47
|
+
def disable_otp(self) -> Dict[str, Any]:
|
|
48
|
+
return self.client._request("DELETE", "/account/otp")
|
|
49
|
+
|
|
50
|
+
# POST /account/password/reset
|
|
51
|
+
def reset_password(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
52
|
+
return self.client._request("POST", "/account/password/reset", json=data)
|
|
53
|
+
|
|
54
|
+
# GET /account/setting
|
|
55
|
+
def get_setting(self) -> Dict[str, Any]:
|
|
56
|
+
return self.client._request("GET", "/account/setting")
|
|
57
|
+
|
|
58
|
+
# PUT /account/setting
|
|
59
|
+
def update_setting(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
60
|
+
return self.client._request("PUT", "/account/setting", json=data)
|
|
61
|
+
|
|
62
|
+
# DELETE /account/setting
|
|
63
|
+
def delete_setting(self, key: str) -> Dict[str, Any]:
|
|
64
|
+
return self.client._request("DELETE", "/account/setting", json={"key": key})
|
|
65
|
+
|
|
66
|
+
# GET /account/sub
|
|
67
|
+
def list_sub_accounts(self) -> Dict[str, Any]:
|
|
68
|
+
return self.client._request("GET", "/account/sub")
|
|
69
|
+
|
|
70
|
+
# PUT /account/sub
|
|
71
|
+
def create_sub_account(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
72
|
+
return self.client._request("PUT", "/account/sub", json=data)
|
|
73
|
+
|
|
74
|
+
# GET /account/sub/{accountId}
|
|
75
|
+
def get_sub_account(self, account_id: str) -> Dict[str, Any]:
|
|
76
|
+
return self.client._request("GET", f"/account/sub/{account_id}")
|
|
77
|
+
|
|
78
|
+
# POST /account/sub/{accountId}
|
|
79
|
+
def update_sub_account(self, account_id: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
80
|
+
return self.client._request("POST", f"/account/sub/{account_id}", json=data)
|
|
81
|
+
|
|
82
|
+
# DELETE /account/sub/{accountId}
|
|
83
|
+
def delete_sub_account(self, account_id: str) -> Dict[str, Any]:
|
|
84
|
+
return self.client._request("DELETE", f"/account/sub/{account_id}")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class AccountUser:
|
|
2
|
+
"""Sub-client for managing account users."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
def list_account_users(self):
|
|
8
|
+
return self.client._request("GET", "/account/user")
|
|
9
|
+
|
|
10
|
+
def create_account_user(self, user_data):
|
|
11
|
+
return self.client._request("PUT", "/account/user", json=user_data)
|
|
12
|
+
|
|
13
|
+
def get_account_user(self, account_user_id):
|
|
14
|
+
return self.client._request("GET", f"/account/user/{account_user_id}")
|
|
15
|
+
|
|
16
|
+
def update_account_user(self, account_user_id, user_data):
|
|
17
|
+
return self.client._request("POST", f"/account/user/{account_user_id}", json=user_data)
|
|
18
|
+
|
|
19
|
+
def delete_account_user(self, account_user_id):
|
|
20
|
+
return self.client._request("DELETE", f"/account/user/{account_user_id}")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class Address:
|
|
2
|
+
"""Sub-client for managing addresses and address units."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- Address CRUD ----
|
|
8
|
+
def list_addresses(self):
|
|
9
|
+
return self.client._request("GET", "/address")
|
|
10
|
+
|
|
11
|
+
def create_address(self, address_data):
|
|
12
|
+
return self.client._request("PUT", "/address", json=address_data)
|
|
13
|
+
|
|
14
|
+
def update_address(self, address_id, address_data):
|
|
15
|
+
return self.client._request("POST", f"/address/{address_id}", json=address_data)
|
|
16
|
+
|
|
17
|
+
def delete_address(self, address_id):
|
|
18
|
+
return self.client._request("DELETE", f"/address/{address_id}")
|
|
19
|
+
|
|
20
|
+
# ---- Address Units ----
|
|
21
|
+
def list_address_units(self, address_id):
|
|
22
|
+
return self.client._request("GET", f"/address/{address_id}/unit")
|
|
23
|
+
|
|
24
|
+
def create_address_unit(self, address_id, unit_data):
|
|
25
|
+
return self.client._request("PUT", f"/address/{address_id}/unit", json=unit_data)
|
|
26
|
+
|
|
27
|
+
def delete_address_units(self, address_id):
|
|
28
|
+
return self.client._request("DELETE", f"/address/{address_id}/unit")
|
|
29
|
+
|
|
30
|
+
def delete_address_unit(self, address_id, unit_id):
|
|
31
|
+
return self.client._request("DELETE", f"/address/{address_id}/unit/{unit_id}")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class AddressReservation:
|
|
2
|
+
"""Sub-client for managing address reservations."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
def list_reservations(self, address_id):
|
|
8
|
+
"""Get a list of address reservations for a specific address."""
|
|
9
|
+
return self.client._request("GET", f"/address/{address_id}/reservation")
|
|
10
|
+
|
|
11
|
+
def issue_reservation(self, address_id, reservation_id):
|
|
12
|
+
"""Issue authorizations for an address reservation."""
|
|
13
|
+
return self.client._request("POST", f"/address/{address_id}/reservation/{reservation_id}/issue")
|
|
14
|
+
|
|
15
|
+
def revoke_reservation(self, address_id, reservation_id):
|
|
16
|
+
"""Revoke authorizations for an address reservation."""
|
|
17
|
+
return self.client._request("POST", f"/address/{address_id}/reservation/{reservation_id}/revoke")
|
|
18
|
+
|
|
19
|
+
def update_reservation_access_times(self, address_id, reservation_id, access_times):
|
|
20
|
+
"""Update access times of a reservation."""
|
|
21
|
+
return self.client._request(
|
|
22
|
+
"POST",
|
|
23
|
+
f"/address/{address_id}/reservation/{reservation_id}/update/accesstimes",
|
|
24
|
+
json=access_times,
|
|
25
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class AddressToken:
|
|
2
|
+
"""Sub-client for managing address tokens."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
def get_token_info(self, token_id):
|
|
8
|
+
"""Get info about a specific address token."""
|
|
9
|
+
return self.client._request("GET", f"/address/token/{token_id}")
|
|
10
|
+
|
|
11
|
+
def get_redeemed_token(self, token_id):
|
|
12
|
+
"""Get info about a redeemed address token."""
|
|
13
|
+
return self.client._request("GET", f"/address/token/{token_id}/redeem")
|
|
14
|
+
|
|
15
|
+
def redeem_token(self, token_id, payload=None):
|
|
16
|
+
"""Redeem an address token."""
|
|
17
|
+
return self.client._request(
|
|
18
|
+
"POST",
|
|
19
|
+
f"/address/token/{token_id}/redeem",
|
|
20
|
+
json=payload or {}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def list_tokens(self, address_id):
|
|
24
|
+
"""Get a list of tokens for a specific address."""
|
|
25
|
+
return self.client._request("GET", f"/address/{address_id}/token")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class AdvancedApi:
|
|
2
|
+
"""Sub-client for managing advanced API functionality."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- Decentralized Webhooks ----
|
|
8
|
+
def list_decentral_webhooks(self):
|
|
9
|
+
"""Get all registered decentral webhooks."""
|
|
10
|
+
return self.client._request("GET", "/api/decentralWebhook")
|
|
11
|
+
|
|
12
|
+
def create_decentral_webhook(self, webhook_data):
|
|
13
|
+
"""Create a new decentral webhook."""
|
|
14
|
+
return self.client._request("PUT", "/api/decentralWebhook", json=webhook_data)
|
|
15
|
+
|
|
16
|
+
def delete_decentral_webhook(self, webhook_id):
|
|
17
|
+
"""Unregister a decentral webhook."""
|
|
18
|
+
return self.client._request("DELETE", f"/api/decentralWebhook/{webhook_id}")
|
|
19
|
+
|
|
20
|
+
def get_webhook_logs(self, api_key_id):
|
|
21
|
+
"""Get a list of webhook logs for a given API key (descending)."""
|
|
22
|
+
return self.client._request("GET", f"/api/key/{api_key_id}/webhook/logs")
|
|
23
|
+
|
|
24
|
+
# ---- Smartlock Advanced Authorizations ----
|
|
25
|
+
def create_smartlock_auth_advanced(self, auth_data):
|
|
26
|
+
"""Creates asynchronous smartlock authorizations."""
|
|
27
|
+
return self.client._request("PUT", "/smartlock/auth/advanced", json=auth_data)
|
|
28
|
+
|
|
29
|
+
def action_smartlock_advanced(self, smartlock_id, action_data):
|
|
30
|
+
"""Smartlock action with callback."""
|
|
31
|
+
return self.client._request(
|
|
32
|
+
"POST", f"/smartlock/{smartlock_id}/action/advanced", json=action_data
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def lock_smartlock_advanced(self, smartlock_id, lock_data=None):
|
|
36
|
+
"""Lock a smartlock (advanced)."""
|
|
37
|
+
return self.client._request(
|
|
38
|
+
"POST", f"/smartlock/{smartlock_id}/action/lock/advanced", json=lock_data or {}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def unlock_smartlock_advanced(self, smartlock_id, unlock_data=None):
|
|
42
|
+
"""Unlock a smartlock (advanced)."""
|
|
43
|
+
return self.client._request(
|
|
44
|
+
"POST", f"/smartlock/{smartlock_id}/action/unlock/advanced", json=unlock_data or {}
|
|
45
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class ApiKey:
|
|
2
|
+
"""Sub-client for managing API keys, advanced keys, and tokens."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- API Keys ----
|
|
8
|
+
def list_api_keys(self):
|
|
9
|
+
return self.client._request("GET", "/api/key")
|
|
10
|
+
|
|
11
|
+
def create_api_key(self, key_data):
|
|
12
|
+
return self.client._request("PUT", "/api/key", json=key_data)
|
|
13
|
+
|
|
14
|
+
def update_api_key(self, api_key_id, key_data):
|
|
15
|
+
return self.client._request("POST", f"/api/key/{api_key_id}", json=key_data)
|
|
16
|
+
|
|
17
|
+
def delete_api_key(self, api_key_id):
|
|
18
|
+
return self.client._request("DELETE", f"/api/key/{api_key_id}")
|
|
19
|
+
|
|
20
|
+
# ---- Advanced API Keys ----
|
|
21
|
+
def get_advanced_api_key(self, api_key_id):
|
|
22
|
+
return self.client._request("GET", f"/api/key/{api_key_id}/advanced")
|
|
23
|
+
|
|
24
|
+
def update_advanced_api_key(self, api_key_id, key_data):
|
|
25
|
+
return self.client._request("POST", f"/api/key/{api_key_id}/advanced", json=key_data)
|
|
26
|
+
|
|
27
|
+
def create_advanced_api_key(self, api_key_id, key_data):
|
|
28
|
+
return self.client._request("PUT", f"/api/key/{api_key_id}/advanced", json=key_data)
|
|
29
|
+
|
|
30
|
+
def delete_advanced_api_key(self, api_key_id):
|
|
31
|
+
return self.client._request("DELETE", f"/api/key/{api_key_id}/advanced")
|
|
32
|
+
|
|
33
|
+
def reactivate_advanced_api_key(self, api_key_id):
|
|
34
|
+
return self.client._request("POST", f"/api/key/{api_key_id}/advanced/reactivate")
|
|
35
|
+
|
|
36
|
+
# ---- API Key Tokens ----
|
|
37
|
+
def list_api_key_tokens(self, api_key_id):
|
|
38
|
+
return self.client._request("GET", f"/api/key/{api_key_id}/token")
|
|
39
|
+
|
|
40
|
+
def create_api_key_token(self, api_key_id, token_data):
|
|
41
|
+
return self.client._request("PUT", f"/api/key/{api_key_id}/token", json=token_data)
|
|
42
|
+
|
|
43
|
+
def update_api_key_token(self, api_key_id, token_id, token_data):
|
|
44
|
+
return self.client._request("POST", f"/api/key/{api_key_id}/token/{token_id}", json=token_data)
|
|
45
|
+
|
|
46
|
+
def delete_api_key_token(self, api_key_id, token_id):
|
|
47
|
+
return self.client._request("DELETE", f"/api/key/{api_key_id}/token/{token_id}")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class Notification:
|
|
2
|
+
"""Sub-client for managing notifications."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- Notification CRUD ----
|
|
8
|
+
def list_notifications(self):
|
|
9
|
+
"""Get all notifications attached to your account."""
|
|
10
|
+
return self.client._request("GET", "/notification")
|
|
11
|
+
|
|
12
|
+
def create_notification(self, notification_data):
|
|
13
|
+
"""Create a notification configuration."""
|
|
14
|
+
return self.client._request("PUT", "/notification", json=notification_data)
|
|
15
|
+
|
|
16
|
+
def get_notification(self, notification_id):
|
|
17
|
+
"""Get a specific notification configuration."""
|
|
18
|
+
return self.client._request("GET", f"/notification/{notification_id}")
|
|
19
|
+
|
|
20
|
+
def update_notification(self, notification_id, notification_data):
|
|
21
|
+
"""Update a notification configuration."""
|
|
22
|
+
return self.client._request("POST", f"/notification/{notification_id}", json=notification_data)
|
|
23
|
+
|
|
24
|
+
def delete_notification(self, notification_id):
|
|
25
|
+
"""Delete a notification configuration."""
|
|
26
|
+
return self.client._request("DELETE", f"/notification/{notification_id}")
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from nukiwebapi.account import Account
|
|
4
|
+
from nukiwebapi.account_user import AccountUser
|
|
5
|
+
from nukiwebapi.address import Address
|
|
6
|
+
from nukiwebapi.address_reservation import AddressReservation
|
|
7
|
+
from nukiwebapi.address_token import AddressToken
|
|
8
|
+
from nukiwebapi.advanced_api import AdvancedApi
|
|
9
|
+
from nukiwebapi.api_key import ApiKey
|
|
10
|
+
from nukiwebapi.company import Company
|
|
11
|
+
from nukiwebapi.notification import Notification
|
|
12
|
+
from nukiwebapi.opener import Opener
|
|
13
|
+
from nukiwebapi.service import Service
|
|
14
|
+
from nukiwebapi.smartlock import Smartlock
|
|
15
|
+
from nukiwebapi.smartlock_auth import SmartlockAuth
|
|
16
|
+
from nukiwebapi.smartlock_log import SmartlockLog
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NukiWebAPI:
|
|
20
|
+
"""Main Nuki Web API client."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, access_token: str, base_url: str = "https://api.nuki.io", smartlock_ids: list[str] = None):
|
|
23
|
+
self.base_url = base_url.rstrip("/")
|
|
24
|
+
self.access_token = access_token
|
|
25
|
+
self.account = Account(self)
|
|
26
|
+
self.account_user = AccountUser(self)
|
|
27
|
+
self.address = Address(self)
|
|
28
|
+
self.address_reservation = AddressReservation(self)
|
|
29
|
+
self.address_token = AddressToken(self)
|
|
30
|
+
self.api_key = ApiKey(self)
|
|
31
|
+
self.smartlocks = self._fetch_smartlocks()
|
|
32
|
+
self.advanced_api = AdvancedApi(self)
|
|
33
|
+
self.company = Company(self)
|
|
34
|
+
self.notification = Notification(self)
|
|
35
|
+
self.opener = Opener(self)
|
|
36
|
+
self.service = Service(self)
|
|
37
|
+
self.smartlock_auth = SmartlockAuth(self)
|
|
38
|
+
self.smartlock_log = SmartlockLog(self)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _fetch_smartlocks(self):
|
|
42
|
+
"""Fetch all smartlocks and create Smartlock objects mapped by ID."""
|
|
43
|
+
response = self._request("GET", "/smartlock")
|
|
44
|
+
smartlocks = {}
|
|
45
|
+
if not isinstance(response, list):
|
|
46
|
+
raise ValueError(f"Expected list from /smartlock, got {type(response)}: {response}")
|
|
47
|
+
|
|
48
|
+
for item in response:
|
|
49
|
+
smartlock_id = item.get("smartlockId")
|
|
50
|
+
if not smartlock_id:
|
|
51
|
+
continue # skip invalid entries
|
|
52
|
+
smartlock = Smartlock(
|
|
53
|
+
client=self,
|
|
54
|
+
smartlock_id=smartlock_id,
|
|
55
|
+
data=item
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
smartlocks[smartlock_id] = smartlock
|
|
59
|
+
|
|
60
|
+
return smartlocks
|
|
61
|
+
|
|
62
|
+
def _request(self, method: str, endpoint: str, **kwargs):
|
|
63
|
+
url = f"{self.base_url}{endpoint}"
|
|
64
|
+
headers = kwargs.pop("headers", {})
|
|
65
|
+
headers["Authorization"] = f"Bearer {self.access_token}"
|
|
66
|
+
headers["Accept"] = "application/json"
|
|
67
|
+
|
|
68
|
+
response = requests.request(method, url, headers=headers, **kwargs)
|
|
69
|
+
response.raise_for_status()
|
|
70
|
+
|
|
71
|
+
# Try parsing JSON only if content is non-empty
|
|
72
|
+
if response.text.strip():
|
|
73
|
+
try:
|
|
74
|
+
return response.json()
|
|
75
|
+
except ValueError:
|
|
76
|
+
return response.text # raw text fallback
|
|
77
|
+
return None # empty response body
|
|
78
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class Opener:
|
|
2
|
+
"""Sub-client for managing intercom/openers."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- Intercom Brands ----
|
|
8
|
+
def list_brands(self):
|
|
9
|
+
"""Get all intercom brands."""
|
|
10
|
+
return self.client._request("GET", "/opener/brand")
|
|
11
|
+
|
|
12
|
+
def get_brand(self, brand_id):
|
|
13
|
+
"""Get a specific intercom brand."""
|
|
14
|
+
return self.client._request("GET", f"/opener/brand/{brand_id}")
|
|
15
|
+
|
|
16
|
+
# ---- Intercom Models ----
|
|
17
|
+
def list_intercoms(self):
|
|
18
|
+
"""Get a list of intercom models."""
|
|
19
|
+
return self.client._request("GET", "/opener/intercom")
|
|
20
|
+
|
|
21
|
+
def get_intercom(self, intercom_id):
|
|
22
|
+
"""Get a specific intercom model."""
|
|
23
|
+
return self.client._request("GET", f"/opener/intercom/{intercom_id}")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Service:
|
|
2
|
+
"""Sub-client for managing services."""
|
|
3
|
+
|
|
4
|
+
def __init__(self, client):
|
|
5
|
+
self.client = client
|
|
6
|
+
|
|
7
|
+
# ---- Services ----
|
|
8
|
+
def list_services(self):
|
|
9
|
+
"""Get a list of services."""
|
|
10
|
+
return self.client._request("GET", "/service")
|
|
11
|
+
|
|
12
|
+
def get_service(self, service_id):
|
|
13
|
+
"""Get a specific service."""
|
|
14
|
+
return self.client._request("GET", f"/service/{service_id}")
|
|
15
|
+
|
|
16
|
+
def link_service(self, service_id, data=None):
|
|
17
|
+
"""Link a service."""
|
|
18
|
+
return self.client._request(
|
|
19
|
+
"POST", f"/service/{service_id}/link", json=data or {}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def sync_service(self, service_id, data=None):
|
|
23
|
+
"""Sync a service."""
|
|
24
|
+
return self.client._request(
|
|
25
|
+
"POST", f"/service/{service_id}/sync", json=data or {}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def unlink_service(self, service_id, data=None):
|
|
29
|
+
"""Unlink a service."""
|
|
30
|
+
return self.client._request(
|
|
31
|
+
"POST", f"/service/{service_id}/unlink", json=data or {}
|
|
32
|
+
)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Smartlock:
|
|
5
|
+
"""Sub-client for managing smartlocks."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, client, smartlock_id, data=None):
|
|
8
|
+
self.client = client
|
|
9
|
+
self.smartlock_id = smartlock_id
|
|
10
|
+
|
|
11
|
+
"""The hexadecimal representation of the smartlock ID.
|
|
12
|
+
It prepends 1-5 depending on the type of the lock.
|
|
13
|
+
Nuki Web API shows only the actual hex ID, omitting the first digit of the Hex ID.
|
|
14
|
+
|
|
15
|
+
E.g. smartlock ID 21913877581 = hex 51A2B3C4D = hex ID 1A2B3C4D
|
|
16
|
+
|
|
17
|
+
The hex_id is to be seen merely as a helper to identify a smartlock more easily,
|
|
18
|
+
the API only relies on the smartlock_id in decimal format as the identifier of
|
|
19
|
+
the smartlock.
|
|
20
|
+
"""
|
|
21
|
+
self.smartlock_hex_id = f'{smartlock_id:x}'[1:].upper()
|
|
22
|
+
self._data = data or {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# --- Metadata properties ---
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def name(self) -> Optional[str]:
|
|
29
|
+
return self._data.get("name") or self._data.get("config", {}).get("name")
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def state(self) -> Optional[Dict[str, Any]]:
|
|
33
|
+
return self._data.get("state")
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def battery_charge(self) -> Optional[int]:
|
|
37
|
+
state = self._data.get("state", {})
|
|
38
|
+
return state.get("batteryCharge")
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def is_locked(self) -> Optional[bool]:
|
|
42
|
+
"""State=1 means locked, 0 unlocked (per API docs)."""
|
|
43
|
+
state = self._data.get("state", {})
|
|
44
|
+
return state.get("state") == 1
|
|
45
|
+
|
|
46
|
+
# --- Data sync ---
|
|
47
|
+
|
|
48
|
+
def refresh(self) -> Dict[str, Any]:
|
|
49
|
+
"""Fetch the latest smartlock data from the API and update object."""
|
|
50
|
+
data = self.client._request("GET", f"/smartlock/{self.smartlock_id}")
|
|
51
|
+
self._data = data
|
|
52
|
+
return data
|
|
53
|
+
# ------------------------
|
|
54
|
+
# Convenience methods (bound instance)
|
|
55
|
+
# ------------------------
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _action(self, smartlock_id: str, action: int) -> Dict[str, Any]:
|
|
59
|
+
|
|
60
|
+
payload = {"action": action}
|
|
61
|
+
return self.client._request(
|
|
62
|
+
"POST",
|
|
63
|
+
f"/smartlock/{smartlock_id}/action",
|
|
64
|
+
json=payload
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def lock(self, full: bool = False) -> Dict[str, Any]:
|
|
69
|
+
"""Lock or full lock the smartlock."""
|
|
70
|
+
if not self.smartlock_id:
|
|
71
|
+
raise ValueError("smartlock_id not set")
|
|
72
|
+
return self._action(self.smartlock_id, 6 if full else 2)
|
|
73
|
+
|
|
74
|
+
def unlock(self) -> Dict[str, Any]:
|
|
75
|
+
"""Unlock the smartlock."""
|
|
76
|
+
if not self.smartlock_id:
|
|
77
|
+
raise ValueError("smartlock_id not set")
|
|
78
|
+
return self._action(self.smartlock_id, "1")
|
|
79
|
+
|
|
80
|
+
def unlatch(self) -> Dict[str, Any]:
|
|
81
|
+
"""Unlatch the smartlock."""
|
|
82
|
+
if not self.smartlock_id:
|
|
83
|
+
raise ValueError("smartlock_id not set")
|
|
84
|
+
return self._action(self.smartlock_id, 3)
|
|
85
|
+
|
|
86
|
+
def lock_and_go(self, unlatch: bool = False) -> Dict[str, Any]:
|
|
87
|
+
"""Lock ’n’ Go, optionally with unlatch."""
|
|
88
|
+
if not self.smartlock_id:
|
|
89
|
+
raise ValueError("smartlock_id not set")
|
|
90
|
+
return self._action(self.smartlock_id, 5 if unlatch else 4)
|
|
91
|
+
|
|
92
|
+
# ------------------------
|
|
93
|
+
# General API methods (for any smartlock)
|
|
94
|
+
# ------------------------
|
|
95
|
+
def bulk_web_config(self, config_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
96
|
+
return self.client._request("POST", "/bulk-web-config", json=config_data)
|
|
97
|
+
|
|
98
|
+
def list_smartlocks(self) -> Dict[str, Any]:
|
|
99
|
+
return self.client._request("GET", "/smartlock")
|
|
100
|
+
|
|
101
|
+
def get_smartlock(self, smartlock_id) -> Dict[str, Any]:
|
|
102
|
+
sid = smartlock_id or self.smartlock_id
|
|
103
|
+
if not sid:
|
|
104
|
+
raise ValueError("smartlock_id not provided")
|
|
105
|
+
return self.client._request("GET", f"/smartlock/{sid}")
|
|
106
|
+
|
|
107
|
+
def update_smartlock(self, smartlock_id, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
108
|
+
sid = smartlock_id or self.smartlock_id
|
|
109
|
+
if not sid:
|
|
110
|
+
raise ValueError("smartlock_id not provided")
|
|
111
|
+
return self.client._request("POST", f"/smartlock/{sid}", json=data or {})
|
|
112
|
+
|
|
113
|
+
def delete_smartlock(self, smartlock_id) -> Dict[str, Any]:
|
|
114
|
+
sid = smartlock_id or self.smartlock_id
|
|
115
|
+
if not sid:
|
|
116
|
+
raise ValueError("smartlock_id not provided")
|
|
117
|
+
return self.client._request("DELETE", f"/smartlock/{sid}")
|
|
118
|
+
|
|
119
|
+
def action_smartlock(self, smartlock_id, action_data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
120
|
+
"""Generic action for any smartlock ID."""
|
|
121
|
+
sid = smartlock_id or self.smartlock_id
|
|
122
|
+
if not sid:
|
|
123
|
+
raise ValueError("smartlock_id not provided")
|
|
124
|
+
return self.client._request("POST", f"/smartlock/{sid}/action", json=action_data or {})
|
|
125
|
+
|
|
126
|
+
def update_admin_pin(self, smartlock_id, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
127
|
+
sid = smartlock_id or self.smartlock_id
|
|
128
|
+
if not sid:
|
|
129
|
+
raise ValueError("smartlock_id not provided")
|
|
130
|
+
return self.client._request("POST", f"/smartlock/{sid}/admin/pin", json=data or {})
|
|
131
|
+
|
|
132
|
+
def update_advanced_config(self, smartlock_id: Optional[str] = None, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
133
|
+
sid = smartlock_id or self.smartlock_id
|
|
134
|
+
if not sid:
|
|
135
|
+
raise ValueError("smartlock_id not provided")
|
|
136
|
+
return self.client._request("POST", f"/smartlock/{sid}/advanced/config", json=data or {})
|
|
137
|
+
|
|
138
|
+
def update_opener_advanced_config(self, smartlock_id: Optional[str] = None, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
139
|
+
sid = smartlock_id or self.smartlock_id
|
|
140
|
+
if not sid:
|
|
141
|
+
raise ValueError("smartlock_id not provided")
|
|
142
|
+
return self.client._request("POST", f"/smartlock/{sid}/advanced/openerconfig", json=data or {})
|
|
143
|
+
|
|
144
|
+
def update_smartdoor_advanced_config(self, smartlock_id: Optional[str] = None, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
145
|
+
sid = smartlock_id or self.smartlock_id
|
|
146
|
+
if not sid:
|
|
147
|
+
raise ValueError("smartlock_id not provided")
|
|
148
|
+
return self.client._request("POST", f"/smartlock/{sid}/advanced/smartdoorconfig", json=data or {})
|
|
149
|
+
|
|
150
|
+
def update_config(self, smartlock_id: Optional[str] = None, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
151
|
+
sid = smartlock_id or self.smartlock_id
|
|
152
|
+
if not sid:
|
|
153
|
+
raise ValueError("smartlock_id not provided")
|
|
154
|
+
return self.client._request("POST", f"/smartlock/{sid}/config", json=data or {})
|
|
155
|
+
|
|
156
|
+
def sync_smartlock(self, smartlock_id: Optional[str] = None) -> Dict[str, Any]:
|
|
157
|
+
sid = smartlock_id or self.smartlock_id
|
|
158
|
+
if not sid:
|
|
159
|
+
raise ValueError("smartlock_id not provided")
|
|
160
|
+
return self.client._request("POST", f"/smartlock/{sid}/sync")
|
|
161
|
+
|
|
162
|
+
def update_web_config(self, smartlock_id: Optional[str] = None, data: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
163
|
+
sid = smartlock_id or self.smartlock_id
|
|
164
|
+
if not sid:
|
|
165
|
+
raise ValueError("smartlock_id not provided")
|
|
166
|
+
return self.client._request("POST", f"/smartlock/{sid}/web/config", json=data or {})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SmartlockAuth:
|
|
5
|
+
"""Sub-client for managing smartlock authorizations."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, client):
|
|
8
|
+
self.client = client
|
|
9
|
+
|
|
10
|
+
# ---- Account-level authorizations ----
|
|
11
|
+
def list_auths(self) -> Dict[str, Any]:
|
|
12
|
+
"""Get a list of smartlock authorizations for your smartlocks."""
|
|
13
|
+
return self.client._request("GET", "/smartlock/auth")
|
|
14
|
+
|
|
15
|
+
def create_auth(self, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
16
|
+
"""Creates asynchronous smartlock authorizations."""
|
|
17
|
+
return self.client._request("PUT", "/smartlock/auth", json=auth_data)
|
|
18
|
+
|
|
19
|
+
def update_auth(self, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
20
|
+
"""Updates smartlock authorizations asynchronously."""
|
|
21
|
+
return self.client._request("POST", "/smartlock/auth", json=auth_data)
|
|
22
|
+
|
|
23
|
+
def delete_auth(self, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
24
|
+
"""Deletes smartlock authorizations asynchronously."""
|
|
25
|
+
return self.client._request("DELETE", "/smartlock/auth", json=auth_data)
|
|
26
|
+
|
|
27
|
+
def list_auths_paged(self, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
28
|
+
"""Get a paginated list of smartlock authorizations for your smartlocks."""
|
|
29
|
+
return self.client._request("GET", "/smartlock/auth/paged", params=params)
|
|
30
|
+
|
|
31
|
+
# ---- Smartlock-specific authorizations ----
|
|
32
|
+
def list_auths_for_smartlock(self, smartlock_id: str) -> Dict[str, Any]:
|
|
33
|
+
"""Get a list of smartlock authorizations for a specific smartlock."""
|
|
34
|
+
return self.client._request("GET", f"/smartlock/{smartlock_id}/auth")
|
|
35
|
+
|
|
36
|
+
def create_auth_for_smartlock(self, smartlock_id: str, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
37
|
+
"""Creates an asynchronous smartlock authorization for a specific smartlock."""
|
|
38
|
+
return self.client._request("PUT", f"/smartlock/{smartlock_id}/auth", json=auth_data)
|
|
39
|
+
|
|
40
|
+
def generate_shared_key_auth(self, smartlock_id: str, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
41
|
+
"""Generate a new smartlock auth with a shared key."""
|
|
42
|
+
return self.client._request(
|
|
43
|
+
"POST", f"/smartlock/{smartlock_id}/auth/advanced/sharedkey", json=auth_data
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def get_auth(self, smartlock_id: str, auth_id: str) -> Dict[str, Any]:
|
|
47
|
+
"""Get a specific smartlock authorization."""
|
|
48
|
+
return self.client._request("GET", f"/smartlock/{smartlock_id}/auth/{auth_id}")
|
|
49
|
+
|
|
50
|
+
def update_auth_for_smartlock(self, smartlock_id: str, auth_id: str, auth_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
51
|
+
"""Updates a specific smartlock authorization asynchronously."""
|
|
52
|
+
return self.client._request(
|
|
53
|
+
"POST", f"/smartlock/{smartlock_id}/auth/{auth_id}", json=auth_data
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def delete_auth_for_smartlock(self, smartlock_id: str, auth_id: str) -> Dict[str, Any]:
|
|
57
|
+
"""Deletes a specific smartlock authorization asynchronously."""
|
|
58
|
+
return self.client._request("DELETE", f"/smartlock/{smartlock_id}/auth/{auth_id}")
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SmartlockLog:
|
|
5
|
+
"""Sub-client for retrieving smartlock logs."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, client):
|
|
8
|
+
self.client = client
|
|
9
|
+
|
|
10
|
+
# ---- Account-level logs ----
|
|
11
|
+
def list_logs(self, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
12
|
+
"""Get a list of smartlock logs for all smartlocks."""
|
|
13
|
+
return self.client._request("GET", "/smartlock/log", params=params)
|
|
14
|
+
|
|
15
|
+
# ---- Smartlock-specific logs ----
|
|
16
|
+
def list_logs_for_smartlock(self, smartlock_id: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
17
|
+
"""Get a list of smartlock logs for a specific smartlock."""
|
|
18
|
+
return self.client._request("GET", f"/smartlock/{smartlock_id}/log", params=params)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nuki-web-api"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python wrapper for the Nuki Web API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{name="barghest89", email="blachosf@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
keywords = ["nuki", "api", "smartlock"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
dependencies = [
|
|
21
|
+
"requests==2.32.5"
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/barghest89/nuki-web-api"
|
|
26
|
+
"Bug Tracker" = "https://github.com/barghest89/nuki-web-api/issues"
|