pyg90alarm 1.13.0__py3-none-any.whl → 1.15.0__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.
- pyg90alarm/__init__.py +31 -4
- pyg90alarm/alarm.py +194 -166
- pyg90alarm/base_cmd.py +60 -52
- pyg90alarm/callback.py +41 -51
- pyg90alarm/config.py +10 -6
- pyg90alarm/const.py +9 -2
- pyg90alarm/definitions/sensors.py +12 -11
- pyg90alarm/device_notifications.py +65 -41
- pyg90alarm/discovery.py +31 -22
- pyg90alarm/entities/device.py +6 -7
- pyg90alarm/entities/sensor.py +180 -126
- pyg90alarm/history.py +43 -47
- pyg90alarm/host_info.py +27 -25
- pyg90alarm/host_status.py +25 -12
- pyg90alarm/paginated_cmd.py +46 -33
- pyg90alarm/paginated_result.py +13 -7
- pyg90alarm/py.typed +0 -0
- pyg90alarm/targeted_discovery.py +98 -39
- pyg90alarm/user_data_crc.py +18 -13
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.15.0.dist-info}/METADATA +4 -3
- pyg90alarm-1.15.0.dist-info/RECORD +27 -0
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.15.0.dist-info}/WHEEL +1 -1
- pyg90alarm-1.13.0.dist-info/RECORD +0 -26
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.15.0.dist-info}/LICENSE +0 -0
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.15.0.dist-info}/top_level.txt +0 -0
pyg90alarm/base_cmd.py
CHANGED
|
@@ -21,43 +21,46 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Provides support for basic commands of G90 alarm panel.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
import logging
|
|
26
26
|
import json
|
|
27
27
|
import asyncio
|
|
28
|
-
from
|
|
28
|
+
from asyncio import Future
|
|
29
|
+
from asyncio.protocols import DatagramProtocol
|
|
30
|
+
from asyncio.transports import DatagramTransport, BaseTransport
|
|
31
|
+
from typing import Optional, Tuple, List, Any
|
|
32
|
+
from dataclasses import dataclass
|
|
29
33
|
from .exceptions import (G90Error, G90TimeoutError)
|
|
34
|
+
from .const import G90Commands
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
_LOGGER = logging.getLogger(__name__)
|
|
38
|
+
G90BaseCommandData = List[Any]
|
|
33
39
|
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
@dataclass
|
|
42
|
+
class G90Header:
|
|
36
43
|
"""
|
|
37
44
|
Represents JSON structure of the header used in alarm panel commands.
|
|
38
|
-
Note that typing.NamedTuple is used (instead of collections.namedtuple) to
|
|
39
|
-
provide support for Python 3.6 and higher, while providing default values.
|
|
40
45
|
|
|
41
46
|
:meta private:
|
|
42
47
|
"""
|
|
43
48
|
code: Optional[int] = None
|
|
44
|
-
data: Optional[
|
|
49
|
+
data: Optional[G90BaseCommandData] = None
|
|
45
50
|
|
|
46
51
|
|
|
47
|
-
class G90BaseCommand:
|
|
52
|
+
class G90BaseCommand(DatagramProtocol):
|
|
48
53
|
"""
|
|
49
|
-
|
|
54
|
+
Implements basic command handling for alarm panel protocol.
|
|
50
55
|
"""
|
|
51
56
|
# pylint: disable=too-many-instance-attributes
|
|
52
57
|
# Lock need to be shared across all of the class instances
|
|
53
58
|
_sk_lock = asyncio.Lock()
|
|
54
59
|
|
|
55
|
-
def __init__(self, host, port, code,
|
|
56
|
-
data=
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
tbd
|
|
60
|
-
"""
|
|
60
|
+
def __init__(self, host: str, port: int, code: G90Commands,
|
|
61
|
+
data: Optional[G90BaseCommandData] = None,
|
|
62
|
+
local_port: Optional[int] = None,
|
|
63
|
+
timeout: float = 3.0, retries: int = 3) -> None:
|
|
61
64
|
# pylint: disable=too-many-arguments
|
|
62
65
|
self._remote_host = host
|
|
63
66
|
self._remote_port = port
|
|
@@ -66,8 +69,10 @@ class G90BaseCommand:
|
|
|
66
69
|
self._timeout = timeout
|
|
67
70
|
self._retries = retries
|
|
68
71
|
self._data = '""'
|
|
69
|
-
self._result =
|
|
70
|
-
self._connection_result
|
|
72
|
+
self._result: G90BaseCommandData = []
|
|
73
|
+
self._connection_result: Optional[
|
|
74
|
+
Future[Tuple[str, int, bytes]]
|
|
75
|
+
] = None
|
|
71
76
|
if data:
|
|
72
77
|
self._data = json.dumps([code, data],
|
|
73
78
|
# No newlines to be inserted
|
|
@@ -78,19 +83,19 @@ class G90BaseCommand:
|
|
|
78
83
|
|
|
79
84
|
# Implementation of datagram protocol,
|
|
80
85
|
# https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols
|
|
81
|
-
def connection_made(self, transport):
|
|
86
|
+
def connection_made(self, transport: BaseTransport) -> None:
|
|
82
87
|
"""
|
|
83
|
-
|
|
88
|
+
Invoked when connection is established.
|
|
84
89
|
"""
|
|
85
90
|
|
|
86
|
-
def connection_lost(self, exc):
|
|
91
|
+
def connection_lost(self, exc: Optional[Exception]) -> None:
|
|
87
92
|
"""
|
|
88
|
-
|
|
93
|
+
Invoked when connection is lost.
|
|
89
94
|
"""
|
|
90
95
|
|
|
91
|
-
def datagram_received(self, data, addr):
|
|
96
|
+
def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None:
|
|
92
97
|
"""
|
|
93
|
-
|
|
98
|
+
Invoked when datagram is received.
|
|
94
99
|
"""
|
|
95
100
|
if asyncio.isfuture(self._connection_result):
|
|
96
101
|
if self._connection_result.done():
|
|
@@ -100,9 +105,9 @@ class G90BaseCommand:
|
|
|
100
105
|
return
|
|
101
106
|
self._connection_result.set_result((*addr, data))
|
|
102
107
|
|
|
103
|
-
def error_received(self, exc):
|
|
108
|
+
def error_received(self, exc: Exception) -> None:
|
|
104
109
|
"""
|
|
105
|
-
|
|
110
|
+
Invoked when error is received.
|
|
106
111
|
"""
|
|
107
112
|
if (
|
|
108
113
|
asyncio.isfuture(self._connection_result) and not
|
|
@@ -110,9 +115,11 @@ class G90BaseCommand:
|
|
|
110
115
|
):
|
|
111
116
|
self._connection_result.set_exception(exc)
|
|
112
117
|
|
|
113
|
-
async def _create_connection(self)
|
|
118
|
+
async def _create_connection(self) -> (
|
|
119
|
+
Tuple[DatagramTransport, DatagramProtocol]
|
|
120
|
+
):
|
|
114
121
|
"""
|
|
115
|
-
|
|
122
|
+
Creates UDP connection to the alarm panel.
|
|
116
123
|
"""
|
|
117
124
|
try:
|
|
118
125
|
loop = asyncio.get_running_loop()
|
|
@@ -121,38 +128,38 @@ class G90BaseCommand:
|
|
|
121
128
|
|
|
122
129
|
_LOGGER.debug('Creating UDP endpoint for %s:%s',
|
|
123
130
|
self.host, self.port)
|
|
124
|
-
|
|
131
|
+
local_addr = None
|
|
125
132
|
if self._local_port:
|
|
126
|
-
|
|
133
|
+
local_addr = ('0.0.0.0', self._local_port)
|
|
127
134
|
|
|
128
135
|
transport, protocol = await loop.create_datagram_endpoint(
|
|
129
136
|
lambda: self,
|
|
130
137
|
remote_addr=(self.host, self.port),
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
allow_broadcast=True,
|
|
139
|
+
local_addr=local_addr)
|
|
133
140
|
|
|
134
|
-
return transport, protocol
|
|
141
|
+
return (transport, protocol)
|
|
135
142
|
|
|
136
|
-
def to_wire(self):
|
|
143
|
+
def to_wire(self) -> bytes:
|
|
137
144
|
"""
|
|
138
|
-
|
|
145
|
+
Returns the command in wire format.
|
|
139
146
|
"""
|
|
140
147
|
wire = bytes(f'ISTART[{self._code},{self._code},{self._data}]IEND\0',
|
|
141
148
|
'utf-8')
|
|
142
149
|
_LOGGER.debug('Encoded to wire format %s', wire)
|
|
143
150
|
return wire
|
|
144
151
|
|
|
145
|
-
def from_wire(self, data):
|
|
152
|
+
def from_wire(self, data: bytes) -> G90BaseCommandData:
|
|
146
153
|
"""
|
|
147
|
-
|
|
154
|
+
Parses the response from the alarm panel.
|
|
148
155
|
"""
|
|
149
156
|
_LOGGER.debug('To be decoded from wire format %s', data)
|
|
150
157
|
self._parse(data.decode('utf-8', errors='ignore'))
|
|
151
|
-
return self._resp.data
|
|
158
|
+
return self._resp.data or []
|
|
152
159
|
|
|
153
|
-
def _parse(self, data):
|
|
160
|
+
def _parse(self, data: str) -> None:
|
|
154
161
|
"""
|
|
155
|
-
|
|
162
|
+
Processes the response from the alarm panel.
|
|
156
163
|
"""
|
|
157
164
|
if not data.startswith('ISTART'):
|
|
158
165
|
raise G90Error('Missing start marker in data')
|
|
@@ -189,39 +196,40 @@ class G90BaseCommand:
|
|
|
189
196
|
f"{self._resp.code}, expected code {self._code}")
|
|
190
197
|
|
|
191
198
|
@property
|
|
192
|
-
def result(self):
|
|
199
|
+
def result(self) -> G90BaseCommandData:
|
|
193
200
|
"""
|
|
194
|
-
|
|
201
|
+
The result of the command.
|
|
195
202
|
"""
|
|
196
203
|
return self._result
|
|
197
204
|
|
|
198
205
|
@property
|
|
199
|
-
def host(self):
|
|
206
|
+
def host(self) -> str:
|
|
200
207
|
"""
|
|
201
|
-
|
|
208
|
+
The hostname/IP address of the alarm panel.
|
|
202
209
|
"""
|
|
203
210
|
return self._remote_host
|
|
204
211
|
|
|
205
212
|
@property
|
|
206
|
-
def port(self):
|
|
213
|
+
def port(self) -> int:
|
|
207
214
|
"""
|
|
208
|
-
|
|
215
|
+
The port of the alarm panel.
|
|
209
216
|
"""
|
|
210
217
|
return self._remote_port
|
|
211
218
|
|
|
212
|
-
async def process(self):
|
|
219
|
+
async def process(self) -> G90BaseCommand:
|
|
213
220
|
"""
|
|
214
|
-
|
|
221
|
+
Processes the command.
|
|
215
222
|
"""
|
|
223
|
+
# Disallow using `NONE` command, which is intended to use by inheriting
|
|
224
|
+
# classes overriding `process()` method
|
|
225
|
+
if self._code == G90Commands.NONE:
|
|
226
|
+
raise G90Error("'NONE' command code is disallowed")
|
|
216
227
|
|
|
217
228
|
transport, _ = await self._create_connection()
|
|
218
229
|
attempts = self._retries
|
|
219
230
|
while True:
|
|
220
231
|
attempts = attempts - 1
|
|
221
|
-
|
|
222
|
-
loop = asyncio.get_running_loop()
|
|
223
|
-
except AttributeError:
|
|
224
|
-
loop = asyncio.get_event_loop()
|
|
232
|
+
loop = asyncio.get_running_loop()
|
|
225
233
|
self._connection_result = loop.create_future()
|
|
226
234
|
async with self._sk_lock:
|
|
227
235
|
_LOGGER.debug('(code %s) Sending request to %s:%s',
|
|
@@ -253,9 +261,9 @@ class G90BaseCommand:
|
|
|
253
261
|
self._result = ret
|
|
254
262
|
return self
|
|
255
263
|
|
|
256
|
-
def __repr__(self):
|
|
264
|
+
def __repr__(self) -> str:
|
|
257
265
|
"""
|
|
258
|
-
|
|
266
|
+
Returns string representation of the command.
|
|
259
267
|
"""
|
|
260
268
|
return f'Command: {self._code}, request: {self._data},' \
|
|
261
269
|
f' response: {self._resp.data}'
|
pyg90alarm/callback.py
CHANGED
|
@@ -21,22 +21,33 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Implements callbacks.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
import asyncio
|
|
26
26
|
from functools import (partial, wraps)
|
|
27
|
+
from asyncio import Task
|
|
28
|
+
from typing import Any, Callable, Coroutine, cast, Optional, Union
|
|
27
29
|
import logging
|
|
28
30
|
|
|
29
31
|
_LOGGER = logging.getLogger(__name__)
|
|
30
32
|
|
|
33
|
+
Callback = Optional[
|
|
34
|
+
Union[
|
|
35
|
+
Callable[..., None],
|
|
36
|
+
Callable[..., Coroutine[None, None, None]],
|
|
37
|
+
]
|
|
38
|
+
]
|
|
39
|
+
|
|
31
40
|
|
|
32
41
|
class G90Callback:
|
|
33
42
|
"""
|
|
34
|
-
|
|
43
|
+
Implements callbacks.
|
|
35
44
|
"""
|
|
36
45
|
@staticmethod
|
|
37
|
-
def invoke(
|
|
46
|
+
def invoke(
|
|
47
|
+
callback: Callback, *args: Any, **kwargs: Any
|
|
48
|
+
) -> None:
|
|
38
49
|
"""
|
|
39
|
-
|
|
50
|
+
Invokes the callback.
|
|
40
51
|
"""
|
|
41
52
|
if not callback:
|
|
42
53
|
return
|
|
@@ -46,73 +57,52 @@ class G90Callback:
|
|
|
46
57
|
callback, args, kwargs)
|
|
47
58
|
|
|
48
59
|
if not asyncio.iscoroutinefunction(callback):
|
|
49
|
-
def async_wrapper(
|
|
60
|
+
def async_wrapper(
|
|
61
|
+
func: Callable[..., None]
|
|
62
|
+
) -> Callable[..., Coroutine[Any, Any, None]]:
|
|
50
63
|
"""
|
|
51
64
|
Wraps the regular callback function into coroutine, so it could
|
|
52
65
|
later be created as async task.
|
|
53
66
|
"""
|
|
54
67
|
@wraps(func)
|
|
55
|
-
async def wrapper(
|
|
68
|
+
async def wrapper(
|
|
69
|
+
*args: Any, **kwds: Any
|
|
70
|
+
) -> None:
|
|
56
71
|
return func(*args, **kwds)
|
|
57
|
-
return wrapper
|
|
58
72
|
|
|
59
|
-
|
|
73
|
+
return cast(Callable[..., Coroutine[Any, Any, None]], wrapper)
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
task = asyncio.create_task(
|
|
76
|
+
async_wrapper(
|
|
77
|
+
cast(Callable[..., None], callback)
|
|
78
|
+
)(*args, **kwargs)
|
|
79
|
+
)
|
|
63
80
|
else:
|
|
64
|
-
|
|
65
|
-
task = asyncio.ensure_future(callback(*args, **kwargs))
|
|
81
|
+
task = asyncio.create_task(callback(*args, **kwargs))
|
|
66
82
|
|
|
67
|
-
def reap_callback_exception(task):
|
|
83
|
+
def reap_callback_exception(task: Task[Any]) -> None:
|
|
68
84
|
"""
|
|
69
85
|
Reaps an exception (if any) from the task logging it, to prevent
|
|
70
86
|
`asyncio` reporting that task exception was never retrieved.
|
|
71
87
|
"""
|
|
72
88
|
exc = task.exception()
|
|
73
89
|
if exc:
|
|
74
|
-
_LOGGER.error(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
90
|
+
_LOGGER.error(
|
|
91
|
+
"Got exception when invoking callback '%s(...)':",
|
|
92
|
+
cast(
|
|
93
|
+
Coroutine[Any, Any, None], task.get_coro()
|
|
94
|
+
).__qualname__,
|
|
95
|
+
exc_info=exc, stack_info=False
|
|
96
|
+
)
|
|
78
97
|
|
|
79
98
|
task.add_done_callback(reap_callback_exception)
|
|
80
99
|
|
|
81
100
|
@staticmethod
|
|
82
|
-
def invoke_delayed(
|
|
101
|
+
def invoke_delayed(
|
|
102
|
+
delay: float, callback: Callable[..., None], *args: Any, **kwargs: Any
|
|
103
|
+
) -> None:
|
|
83
104
|
"""
|
|
84
|
-
|
|
105
|
+
Invokes the callback after a delay.
|
|
85
106
|
"""
|
|
86
|
-
|
|
87
|
-
loop = asyncio.get_running_loop()
|
|
88
|
-
else:
|
|
89
|
-
# Python 3.6 has no `get_running_loop`, only `get_event_loop`
|
|
90
|
-
loop = asyncio.get_event_loop()
|
|
107
|
+
loop = asyncio.get_running_loop()
|
|
91
108
|
loop.call_later(delay, partial(callback, *args, **kwargs))
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def async_as_sync(func):
|
|
95
|
-
"""
|
|
96
|
-
Invokes an async function as regular one via :py:func:`G90Callback.invoke`.
|
|
97
|
-
One of possible use cases is implementing property setter for async code,
|
|
98
|
-
where the function could be used an decorator:
|
|
99
|
-
|
|
100
|
-
.. code-block:: python
|
|
101
|
-
|
|
102
|
-
@property
|
|
103
|
-
async def a_property(...):
|
|
104
|
-
...
|
|
105
|
-
|
|
106
|
-
@a_property.setter
|
|
107
|
-
@async_as_sync
|
|
108
|
-
async def a_property(...):
|
|
109
|
-
...
|
|
110
|
-
|
|
111
|
-
Since the function internally submits the wrapped async code as
|
|
112
|
-
:py:mod:`asyncio` task, it execution isn't guaranteed as the program could
|
|
113
|
-
be terminated earlier that it is processed in the loop.
|
|
114
|
-
"""
|
|
115
|
-
@wraps(func)
|
|
116
|
-
def wrapper(*args, **kwargs):
|
|
117
|
-
return G90Callback.invoke(func, *args, **kwargs)
|
|
118
|
-
return wrapper
|
pyg90alarm/config.py
CHANGED
|
@@ -21,12 +21,15 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Represents various configuration aspects of the alarm panel.
|
|
23
23
|
"""
|
|
24
|
+
from __future__ import annotations
|
|
24
25
|
from enum import IntFlag
|
|
25
|
-
from
|
|
26
|
+
from dataclasses import dataclass
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class G90AlertConfigFlags(IntFlag):
|
|
29
|
-
"""
|
|
30
|
+
"""
|
|
31
|
+
Alert configuration flags, used bitwise
|
|
32
|
+
"""
|
|
30
33
|
AC_POWER_FAILURE = 1
|
|
31
34
|
AC_POWER_RECOVER = 2
|
|
32
35
|
ARM_DISARM = 4
|
|
@@ -41,15 +44,16 @@ class G90AlertConfigFlags(IntFlag):
|
|
|
41
44
|
UNKNOWN2 = 8192
|
|
42
45
|
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
@dataclass
|
|
48
|
+
class G90AlertConfig:
|
|
45
49
|
"""
|
|
46
50
|
Represents alert configuration as received from the alarm panel.
|
|
47
51
|
"""
|
|
52
|
+
flags_data: int
|
|
48
53
|
|
|
49
54
|
@property
|
|
50
|
-
def flags(self):
|
|
55
|
+
def flags(self) -> G90AlertConfigFlags:
|
|
51
56
|
"""
|
|
52
|
-
:return:
|
|
53
|
-
symbolic names to corresponding flag bits
|
|
57
|
+
:return: Symbolic names for corresponding flag bits
|
|
54
58
|
"""
|
|
55
59
|
return G90AlertConfigFlags(self.flags_data)
|
pyg90alarm/const.py
CHANGED
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Definies different constants for G90 alarm panel.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
from enum import IntEnum
|
|
26
|
+
from typing import Optional
|
|
26
27
|
|
|
27
28
|
REMOTE_PORT = 12368
|
|
28
29
|
REMOTE_TARGETED_DISCOVERY_PORT = 12900
|
|
@@ -40,7 +41,7 @@ class G90Commands(IntEnum):
|
|
|
40
41
|
comprehensive or complete.
|
|
41
42
|
"""
|
|
42
43
|
|
|
43
|
-
def __new__(cls, value, doc=None):
|
|
44
|
+
def __new__(cls, value: int, doc: Optional[str] = None) -> G90Commands:
|
|
44
45
|
"""
|
|
45
46
|
Allows to set the docstring along with the value to enum entry.
|
|
46
47
|
"""
|
|
@@ -49,6 +50,12 @@ class G90Commands(IntEnum):
|
|
|
49
50
|
obj.__doc__ = doc
|
|
50
51
|
return obj
|
|
51
52
|
|
|
53
|
+
NONE = (0, """
|
|
54
|
+
Pseudo command, to be used for proper typing with subclasses of
|
|
55
|
+
`G90BaseCommand` invoking its constructor but implementing special
|
|
56
|
+
processing
|
|
57
|
+
""")
|
|
58
|
+
|
|
52
59
|
# Host status
|
|
53
60
|
GETHOSTSTATUS = (100, 'Get host status')
|
|
54
61
|
SETHOSTSTATUS = (101, 'Set host status')
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
Sensor definitions for G90 devices, required when modifying them since writing
|
|
23
23
|
a sensor to the device requires values not present on read.
|
|
24
24
|
"""
|
|
25
|
-
from
|
|
25
|
+
from typing import NamedTuple
|
|
26
26
|
from enum import IntEnum
|
|
27
27
|
|
|
28
28
|
|
|
@@ -44,21 +44,22 @@ class SensorRwMode(IntEnum):
|
|
|
44
44
|
READ_WRITE = 2
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class SensorDefinition(
|
|
48
|
-
namedtuple(
|
|
49
|
-
'SensorDefinition', [
|
|
50
|
-
'type', 'subtype', 'rx', 'tx',
|
|
51
|
-
'private_data', 'rwMode', 'matchMode',
|
|
52
|
-
]
|
|
53
|
-
)
|
|
54
|
-
):
|
|
47
|
+
class SensorDefinition(NamedTuple):
|
|
55
48
|
"""
|
|
56
49
|
Holds sensor definition data.
|
|
57
50
|
"""
|
|
51
|
+
type: int
|
|
52
|
+
subtype: int
|
|
53
|
+
rx: int
|
|
54
|
+
tx: int
|
|
55
|
+
private_data: str
|
|
56
|
+
rwMode: SensorRwMode
|
|
57
|
+
matchMode: SensorMatchMode
|
|
58
|
+
|
|
58
59
|
@property
|
|
59
|
-
def reserved_data(self):
|
|
60
|
+
def reserved_data(self) -> int:
|
|
60
61
|
"""
|
|
61
|
-
|
|
62
|
+
Sensor's 'reserved_data' field to be written, combined of match
|
|
62
63
|
and RW mode values bitwise.
|
|
63
64
|
"""
|
|
64
65
|
return self.matchMode.value << 4 | self.rwMode.value
|