HAP-python 4.9.0__tar.gz → 4.9.2__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.
- {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/PKG-INFO +10 -5
- {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/SOURCES.txt +17 -1
- {HAP-python-4.9.0 → hap_python-4.9.2}/PKG-INFO +10 -5
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/accessory.py +8 -3
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/accessory_driver.py +5 -2
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/const.py +1 -1
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_crypto.py +3 -6
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_protocol.py +17 -11
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_server.py +20 -12
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/iid_manager.py +16 -9
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyproject.toml +1 -0
- hap_python-4.9.2/tests/test_accessory.py +588 -0
- hap_python-4.9.2/tests/test_accessory_driver.py +1235 -0
- hap_python-4.9.2/tests/test_camera.py +143 -0
- hap_python-4.9.2/tests/test_characteristic.py +574 -0
- hap_python-4.9.2/tests/test_encoder.py +121 -0
- hap_python-4.9.2/tests/test_hap_crypto.py +33 -0
- hap_python-4.9.2/tests/test_hap_handler.py +1042 -0
- hap_python-4.9.2/tests/test_hap_protocol.py +802 -0
- hap_python-4.9.2/tests/test_hap_server.py +198 -0
- hap_python-4.9.2/tests/test_hsrp.py +65 -0
- hap_python-4.9.2/tests/test_iid_manager.py +58 -0
- hap_python-4.9.2/tests/test_loader.py +75 -0
- hap_python-4.9.2/tests/test_service.py +241 -0
- hap_python-4.9.2/tests/test_state.py +119 -0
- hap_python-4.9.2/tests/test_tlv.py +29 -0
- hap_python-4.9.2/tests/test_util.py +48 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/dependency_links.txt +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/requires.txt +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/HAP_python.egg-info/top_level.txt +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/LICENSE +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/MANIFEST.in +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/README.md +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/__init__.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/camera.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/characteristic.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/encoder.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_event.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hap_handler.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/hsrp.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/loader.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/params.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/characteristics.json +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/services.json +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/resources/snapshot.jpg +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/service.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/state.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/tlv.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/pyhap/util.py +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/setup.cfg +0 -0
- {HAP-python-4.9.0 → hap_python-4.9.2}/setup.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: HAP-python
|
|
3
|
-
Version: 4.9.
|
|
3
|
+
Version: 4.9.2
|
|
4
4
|
Summary: HomeKit Accessory Protocol implementation in python
|
|
5
5
|
Home-page: https://github.com/ikalchev/HAP-python
|
|
6
6
|
License: Apache License 2.0
|
|
7
7
|
Project-URL: Bug Reports, https://github.com/ikalchev/HAP-python/issues
|
|
8
8
|
Project-URL: Documentation, http://hap-python.readthedocs.io/en/latest/
|
|
9
9
|
Project-URL: Source, https://github.com/ikalchev/HAP-python/tree/master
|
|
10
|
-
Platform: UNKNOWN
|
|
11
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
13
12
|
Classifier: Intended Audience :: End Users/Desktop
|
|
@@ -24,8 +23,16 @@ Classifier: Topic :: Home Automation
|
|
|
24
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
24
|
Requires-Python: >=3.7
|
|
26
25
|
Description-Content-Type: text/markdown
|
|
27
|
-
Provides-Extra: QRCode
|
|
28
26
|
License-File: LICENSE
|
|
27
|
+
Requires-Dist: async_timeout
|
|
28
|
+
Requires-Dist: cryptography
|
|
29
|
+
Requires-Dist: chacha20poly1305-reuseable
|
|
30
|
+
Requires-Dist: orjson>=3.7.2
|
|
31
|
+
Requires-Dist: zeroconf>=0.36.2
|
|
32
|
+
Requires-Dist: h11
|
|
33
|
+
Provides-Extra: qrcode
|
|
34
|
+
Requires-Dist: base36; extra == "qrcode"
|
|
35
|
+
Requires-Dist: pyqrcode; extra == "qrcode"
|
|
29
36
|
|
|
30
37
|
[](https://badge.fury.io/py/HAP-python) [](https://github.com/ikalchev/HAP-python) [](https://codecov.io/gh/ikalchev/HAP-python) [](https://pypi.python.org/pypi/HAP-python/) [](http://hap-python.readthedocs.io/en/latest/?badge=latest) [](https://pepy.tech/project/hap-python)
|
|
31
38
|
# HAP-python
|
|
@@ -302,5 +309,3 @@ please report and I will try to fix them.
|
|
|
302
309
|
Suggestions are always welcome.
|
|
303
310
|
|
|
304
311
|
Have fun!
|
|
305
|
-
|
|
306
|
-
|
|
@@ -31,4 +31,20 @@ pyhap/tlv.py
|
|
|
31
31
|
pyhap/util.py
|
|
32
32
|
pyhap/resources/characteristics.json
|
|
33
33
|
pyhap/resources/services.json
|
|
34
|
-
pyhap/resources/snapshot.jpg
|
|
34
|
+
pyhap/resources/snapshot.jpg
|
|
35
|
+
tests/test_accessory.py
|
|
36
|
+
tests/test_accessory_driver.py
|
|
37
|
+
tests/test_camera.py
|
|
38
|
+
tests/test_characteristic.py
|
|
39
|
+
tests/test_encoder.py
|
|
40
|
+
tests/test_hap_crypto.py
|
|
41
|
+
tests/test_hap_handler.py
|
|
42
|
+
tests/test_hap_protocol.py
|
|
43
|
+
tests/test_hap_server.py
|
|
44
|
+
tests/test_hsrp.py
|
|
45
|
+
tests/test_iid_manager.py
|
|
46
|
+
tests/test_loader.py
|
|
47
|
+
tests/test_service.py
|
|
48
|
+
tests/test_state.py
|
|
49
|
+
tests/test_tlv.py
|
|
50
|
+
tests/test_util.py
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: HAP-python
|
|
3
|
-
Version: 4.9.
|
|
3
|
+
Version: 4.9.2
|
|
4
4
|
Summary: HomeKit Accessory Protocol implementation in python
|
|
5
5
|
Home-page: https://github.com/ikalchev/HAP-python
|
|
6
6
|
License: Apache License 2.0
|
|
7
7
|
Project-URL: Bug Reports, https://github.com/ikalchev/HAP-python/issues
|
|
8
8
|
Project-URL: Documentation, http://hap-python.readthedocs.io/en/latest/
|
|
9
9
|
Project-URL: Source, https://github.com/ikalchev/HAP-python/tree/master
|
|
10
|
-
Platform: UNKNOWN
|
|
11
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
13
12
|
Classifier: Intended Audience :: End Users/Desktop
|
|
@@ -24,8 +23,16 @@ Classifier: Topic :: Home Automation
|
|
|
24
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
24
|
Requires-Python: >=3.7
|
|
26
25
|
Description-Content-Type: text/markdown
|
|
27
|
-
Provides-Extra: QRCode
|
|
28
26
|
License-File: LICENSE
|
|
27
|
+
Requires-Dist: async_timeout
|
|
28
|
+
Requires-Dist: cryptography
|
|
29
|
+
Requires-Dist: chacha20poly1305-reuseable
|
|
30
|
+
Requires-Dist: orjson>=3.7.2
|
|
31
|
+
Requires-Dist: zeroconf>=0.36.2
|
|
32
|
+
Requires-Dist: h11
|
|
33
|
+
Provides-Extra: qrcode
|
|
34
|
+
Requires-Dist: base36; extra == "qrcode"
|
|
35
|
+
Requires-Dist: pyqrcode; extra == "qrcode"
|
|
29
36
|
|
|
30
37
|
[](https://badge.fury.io/py/HAP-python) [](https://github.com/ikalchev/HAP-python) [](https://codecov.io/gh/ikalchev/HAP-python) [](https://pypi.python.org/pypi/HAP-python/) [](http://hap-python.readthedocs.io/en/latest/?badge=latest) [](https://pepy.tech/project/hap-python)
|
|
31
38
|
# HAP-python
|
|
@@ -302,5 +309,3 @@ please report and I will try to fix them.
|
|
|
302
309
|
Suggestions are always welcome.
|
|
303
310
|
|
|
304
311
|
Have fun!
|
|
305
|
-
|
|
306
|
-
|
|
@@ -207,7 +207,7 @@ class Accessory:
|
|
|
207
207
|
int(self.driver.state.pincode.replace(b"-", b""), 10) & 0x7FFFFFFF
|
|
208
208
|
) # pincode
|
|
209
209
|
|
|
210
|
-
encoded_payload = base36.dumps(payload).upper()
|
|
210
|
+
encoded_payload = base36.dumps(payload).upper() # pylint: disable=possibly-used-before-assignment
|
|
211
211
|
encoded_payload = encoded_payload.rjust(9, "0")
|
|
212
212
|
|
|
213
213
|
return "X-HM://" + encoded_payload + self.driver.state.setup_id
|
|
@@ -241,7 +241,9 @@ class Accessory:
|
|
|
241
241
|
"""
|
|
242
242
|
return {
|
|
243
243
|
HAP_REPR_AID: self.aid,
|
|
244
|
-
HAP_REPR_SERVICES: [
|
|
244
|
+
HAP_REPR_SERVICES: [
|
|
245
|
+
s.to_HAP(include_value=include_value) for s in self.services
|
|
246
|
+
],
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
def setup_message(self):
|
|
@@ -391,7 +393,10 @@ class Bridge(Accessory):
|
|
|
391
393
|
|
|
392
394
|
.. seealso:: Accessory.to_HAP
|
|
393
395
|
"""
|
|
394
|
-
return [
|
|
396
|
+
return [
|
|
397
|
+
acc.to_HAP(include_value=include_value)
|
|
398
|
+
for acc in (super(), *self.accessories.values())
|
|
399
|
+
]
|
|
395
400
|
|
|
396
401
|
def get_characteristic(self, aid: int, iid: int) -> Optional["Characteristic"]:
|
|
397
402
|
""".. seealso:: Accessory.to_HAP"""
|
|
@@ -323,7 +323,7 @@ class AccessoryDriver:
|
|
|
323
323
|
and os.name != "nt"
|
|
324
324
|
):
|
|
325
325
|
logger.debug("Setting child watcher")
|
|
326
|
-
watcher = asyncio.SafeChildWatcher()
|
|
326
|
+
watcher = asyncio.SafeChildWatcher() # pylint: disable=deprecated-class
|
|
327
327
|
watcher.attach_loop(self.loop)
|
|
328
328
|
asyncio.set_child_watcher(watcher)
|
|
329
329
|
else:
|
|
@@ -642,16 +642,19 @@ class AccessoryDriver:
|
|
|
642
642
|
tmp_filename = None
|
|
643
643
|
try:
|
|
644
644
|
temp_dir = os.path.dirname(self.persist_file)
|
|
645
|
+
logger.debug("Creating temp persist file in '%s'", temp_dir)
|
|
645
646
|
with tempfile.NamedTemporaryFile(
|
|
646
647
|
mode="w", dir=temp_dir, delete=False
|
|
647
648
|
) as file_handle:
|
|
648
649
|
tmp_filename = file_handle.name
|
|
650
|
+
logger.debug("Created temp persist file '%s' named '%s'", file_handle, tmp_filename)
|
|
649
651
|
self.encoder.persist(file_handle, self.state)
|
|
650
652
|
if (
|
|
651
653
|
os.name == "nt"
|
|
652
654
|
): # Or `[WinError 5] Access Denied` will be raised on Windows
|
|
653
655
|
os.chmod(tmp_filename, 0o644)
|
|
654
|
-
os.
|
|
656
|
+
if os.path.exists(self.persist_file):
|
|
657
|
+
os.chmod(self.persist_file, 0o644)
|
|
655
658
|
os.replace(tmp_filename, self.persist_file)
|
|
656
659
|
except Exception: # pylint: disable=broad-except
|
|
657
660
|
logger.exception("Failed to persist accessory state")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""This module contains constants used by other modules."""
|
|
2
2
|
MAJOR_VERSION = 4
|
|
3
3
|
MINOR_VERSION = 9
|
|
4
|
-
PATCH_VERSION =
|
|
4
|
+
PATCH_VERSION = 2
|
|
5
5
|
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
|
6
6
|
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
|
7
7
|
REQUIRED_PYTHON_VER = (3, 7)
|
|
@@ -3,7 +3,7 @@ from functools import partial
|
|
|
3
3
|
import logging
|
|
4
4
|
import struct
|
|
5
5
|
from struct import Struct
|
|
6
|
-
from typing import List
|
|
6
|
+
from typing import Iterable, List
|
|
7
7
|
|
|
8
8
|
from chacha20poly1305_reuseable import ChaCha20Poly1305Reusable as ChaCha20Poly1305
|
|
9
9
|
from cryptography.hazmat.backends import default_backend
|
|
@@ -112,7 +112,7 @@ class HAPCrypto:
|
|
|
112
112
|
|
|
113
113
|
return result
|
|
114
114
|
|
|
115
|
-
def encrypt(self, data: bytes) -> bytes:
|
|
115
|
+
def encrypt(self, data: bytes) -> Iterable[bytes]:
|
|
116
116
|
"""Encrypt and send the return bytes."""
|
|
117
117
|
result: List[bytes] = []
|
|
118
118
|
offset = 0
|
|
@@ -127,7 +127,4 @@ class HAPCrypto:
|
|
|
127
127
|
offset += length
|
|
128
128
|
self._out_count += 1
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
# as this is much faster than generating an new immutable
|
|
132
|
-
# byte string each time.
|
|
133
|
-
return b"".join(result)
|
|
130
|
+
return result
|
|
@@ -46,7 +46,7 @@ class HAPServerProtocol(asyncio.Protocol):
|
|
|
46
46
|
connections: Dict[str, "HAPServerProtocol"],
|
|
47
47
|
accessory_driver: "AccessoryDriver",
|
|
48
48
|
) -> None:
|
|
49
|
-
self.loop
|
|
49
|
+
self.loop = loop
|
|
50
50
|
self.conn = h11.Connection(h11.SERVER)
|
|
51
51
|
self.connections = connections
|
|
52
52
|
self.accessory_driver = accessory_driver
|
|
@@ -55,7 +55,7 @@ class HAPServerProtocol(asyncio.Protocol):
|
|
|
55
55
|
self.transport: Optional[asyncio.Transport] = None
|
|
56
56
|
|
|
57
57
|
self.request: Optional[h11.Request] = None
|
|
58
|
-
self.request_body:
|
|
58
|
+
self.request_body: List[bytes] = []
|
|
59
59
|
self.response: Optional[HAPResponse] = None
|
|
60
60
|
|
|
61
61
|
self.last_activity: Optional[float] = None
|
|
@@ -104,7 +104,7 @@ class HAPServerProtocol(asyncio.Protocol):
|
|
|
104
104
|
self.handler.client_uuid,
|
|
105
105
|
data,
|
|
106
106
|
)
|
|
107
|
-
self.transport.
|
|
107
|
+
self.transport.writelines(result)
|
|
108
108
|
else:
|
|
109
109
|
logger.debug(
|
|
110
110
|
"%s (%s): Send unencrypted: %s",
|
|
@@ -246,27 +246,33 @@ class HAPServerProtocol(asyncio.Protocol):
|
|
|
246
246
|
logger.debug(
|
|
247
247
|
"%s (%s): h11 Event: %s", self.peername, self.handler.client_uuid, event
|
|
248
248
|
)
|
|
249
|
-
if event
|
|
249
|
+
if event is h11.NEED_DATA:
|
|
250
250
|
return False
|
|
251
251
|
|
|
252
252
|
if event is h11.PAUSED:
|
|
253
253
|
self.conn.start_next_cycle()
|
|
254
254
|
return True
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
event_type = type(event)
|
|
257
|
+
if event_type is h11.ConnectionClosed:
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
if event_type is h11.Request:
|
|
257
261
|
self.request = event
|
|
258
|
-
self.request_body =
|
|
262
|
+
self.request_body = []
|
|
259
263
|
return True
|
|
260
264
|
|
|
261
|
-
if
|
|
262
|
-
|
|
265
|
+
if event_type is h11.Data:
|
|
266
|
+
if TYPE_CHECKING:
|
|
267
|
+
assert isinstance(event, h11.Data) # nosec
|
|
268
|
+
self.request_body.append(event.data)
|
|
263
269
|
return True
|
|
264
270
|
|
|
265
|
-
if
|
|
266
|
-
response = self.handler.dispatch(self.request,
|
|
271
|
+
if event_type is h11.EndOfMessage:
|
|
272
|
+
response = self.handler.dispatch(self.request, b"".join(self.request_body))
|
|
267
273
|
self._process_response(response)
|
|
268
274
|
self.request = None
|
|
269
|
-
self.request_body =
|
|
275
|
+
self.request_body = []
|
|
270
276
|
return True
|
|
271
277
|
|
|
272
278
|
return self._handle_invalid_conn_state(f"Unexpected event: {event}")
|
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
The HAPServer is the point of contact to and from the world.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import asyncio
|
|
6
7
|
import logging
|
|
7
8
|
import time
|
|
9
|
+
from typing import TYPE_CHECKING, Dict, Optional, Tuple
|
|
8
10
|
|
|
9
11
|
from .hap_protocol import HAPServerProtocol
|
|
10
12
|
from .util import callback
|
|
11
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .accessory_driver import AccessoryDriver
|
|
16
|
+
|
|
12
17
|
logger = logging.getLogger(__name__)
|
|
13
18
|
|
|
14
|
-
IDLE_CONNECTION_CHECK_INTERVAL_SECONDS =
|
|
19
|
+
IDLE_CONNECTION_CHECK_INTERVAL_SECONDS = 300
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
class HAPServer:
|
|
@@ -28,17 +33,18 @@ class HAPServer:
|
|
|
28
33
|
implements exclusive access to the send methods.
|
|
29
34
|
"""
|
|
30
35
|
|
|
31
|
-
def __init__(
|
|
36
|
+
def __init__(
|
|
37
|
+
self, addr_port: Tuple[str, int], accessory_handler: "AccessoryDriver"
|
|
38
|
+
) -> None:
|
|
32
39
|
"""Create a HAP Server."""
|
|
33
40
|
self._addr_port = addr_port
|
|
34
|
-
self.connections = {}
|
|
41
|
+
self.connections: Dict[Tuple[str, int], HAPServerProtocol] = {}
|
|
35
42
|
self.accessory_handler = accessory_handler
|
|
36
|
-
self.server = None
|
|
37
|
-
self.
|
|
38
|
-
self.
|
|
39
|
-
self.loop = None
|
|
43
|
+
self.server: Optional[asyncio.Server] = None
|
|
44
|
+
self._connection_cleanup: Optional[asyncio.TimerHandle] = None
|
|
45
|
+
self.loop: Optional[asyncio.AbstractEventLoop] = None
|
|
40
46
|
|
|
41
|
-
async def async_start(self, loop):
|
|
47
|
+
async def async_start(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
42
48
|
"""Start the http-hap server."""
|
|
43
49
|
self.loop = loop
|
|
44
50
|
self.server = await loop.create_server(
|
|
@@ -49,7 +55,7 @@ class HAPServer:
|
|
|
49
55
|
self.async_cleanup_connections()
|
|
50
56
|
|
|
51
57
|
@callback
|
|
52
|
-
def async_cleanup_connections(self):
|
|
58
|
+
def async_cleanup_connections(self) -> None:
|
|
53
59
|
"""Cleanup stale connections."""
|
|
54
60
|
now = time.time()
|
|
55
61
|
for hap_proto in list(self.connections.values()):
|
|
@@ -59,7 +65,7 @@ class HAPServer:
|
|
|
59
65
|
)
|
|
60
66
|
|
|
61
67
|
@callback
|
|
62
|
-
def async_stop(self):
|
|
68
|
+
def async_stop(self) -> None:
|
|
63
69
|
"""Stop the server.
|
|
64
70
|
|
|
65
71
|
This method must be run in the event loop.
|
|
@@ -70,10 +76,12 @@ class HAPServer:
|
|
|
70
76
|
self.server.close()
|
|
71
77
|
self.connections.clear()
|
|
72
78
|
|
|
73
|
-
def push_event(
|
|
79
|
+
def push_event(
|
|
80
|
+
self, data: bytes, client_addr: Tuple[str, int], immediate: bool = False
|
|
81
|
+
) -> bool:
|
|
74
82
|
"""Queue an event to the current connection with the provided data.
|
|
75
83
|
|
|
76
|
-
:param data: The
|
|
84
|
+
:param data: The characteristic changes
|
|
77
85
|
:type data: dict
|
|
78
86
|
|
|
79
87
|
:param client_addr: A client (address, port) tuple to which to send the data.
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
"""Module for the IIDManager class."""
|
|
2
2
|
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, Optional, Union
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from .characteristic import Characteristic
|
|
7
|
+
from .service import Service
|
|
8
|
+
|
|
9
|
+
ServiceOrCharType = Union[Service, Characteristic]
|
|
3
10
|
|
|
4
11
|
logger = logging.getLogger(__name__)
|
|
5
12
|
|
|
@@ -7,13 +14,13 @@ logger = logging.getLogger(__name__)
|
|
|
7
14
|
class IIDManager:
|
|
8
15
|
"""Maintains a mapping between Service/Characteristic objects and IIDs."""
|
|
9
16
|
|
|
10
|
-
def __init__(self):
|
|
17
|
+
def __init__(self) -> None:
|
|
11
18
|
"""Initialize an empty instance."""
|
|
12
19
|
self.counter = 0
|
|
13
|
-
self.iids = {}
|
|
14
|
-
self.objs = {}
|
|
20
|
+
self.iids: Dict["ServiceOrCharType", int] = {}
|
|
21
|
+
self.objs: Dict[int, "ServiceOrCharType"] = {}
|
|
15
22
|
|
|
16
|
-
def assign(self, obj):
|
|
23
|
+
def assign(self, obj: "ServiceOrCharType") -> None:
|
|
17
24
|
"""Assign an IID to given object. Print warning if already assigned.
|
|
18
25
|
|
|
19
26
|
:param obj: The object that will be assigned an IID.
|
|
@@ -32,7 +39,7 @@ class IIDManager:
|
|
|
32
39
|
self.iids[obj] = iid
|
|
33
40
|
self.objs[iid] = obj
|
|
34
41
|
|
|
35
|
-
def get_iid_for_obj(self, obj):
|
|
42
|
+
def get_iid_for_obj(self, obj: "ServiceOrCharType") -> int:
|
|
36
43
|
"""Get the IID for the given object.
|
|
37
44
|
|
|
38
45
|
Override this method to provide custom IID assignment.
|
|
@@ -40,15 +47,15 @@ class IIDManager:
|
|
|
40
47
|
self.counter += 1
|
|
41
48
|
return self.counter
|
|
42
49
|
|
|
43
|
-
def get_obj(self, iid):
|
|
50
|
+
def get_obj(self, iid: int) -> "ServiceOrCharType":
|
|
44
51
|
"""Get the object that is assigned the given IID."""
|
|
45
52
|
return self.objs.get(iid)
|
|
46
53
|
|
|
47
|
-
def get_iid(self, obj):
|
|
54
|
+
def get_iid(self, obj: "ServiceOrCharType") -> int:
|
|
48
55
|
"""Get the IID assigned to the given object."""
|
|
49
56
|
return self.iids.get(obj)
|
|
50
57
|
|
|
51
|
-
def remove_obj(self, obj):
|
|
58
|
+
def remove_obj(self, obj: "ServiceOrCharType") -> Optional[int]:
|
|
52
59
|
"""Remove an object from the IID list."""
|
|
53
60
|
iid = self.iids.pop(obj, None)
|
|
54
61
|
if iid is None:
|
|
@@ -57,7 +64,7 @@ class IIDManager:
|
|
|
57
64
|
del self.objs[iid]
|
|
58
65
|
return iid
|
|
59
66
|
|
|
60
|
-
def remove_iid(self, iid):
|
|
67
|
+
def remove_iid(self, iid: int) -> Optional["ServiceOrCharType"]:
|
|
61
68
|
"""Remove an object with an IID from the IID list."""
|
|
62
69
|
obj = self.objs.pop(iid, None)
|
|
63
70
|
if obj is None:
|