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/entities/sensor.py
CHANGED
|
@@ -21,48 +21,67 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Provides interface to sensors of G90 alarm panel.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
import logging
|
|
26
|
-
from
|
|
26
|
+
from dataclasses import dataclass, asdict, astuple
|
|
27
|
+
from typing import (
|
|
28
|
+
Any, Optional, TYPE_CHECKING, Dict
|
|
29
|
+
)
|
|
30
|
+
|
|
27
31
|
from enum import IntEnum, IntFlag
|
|
28
|
-
from ..definitions.sensors import SENSOR_DEFINITIONS
|
|
32
|
+
from ..definitions.sensors import SENSOR_DEFINITIONS, SensorDefinition
|
|
29
33
|
from ..const import G90Commands
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from ..alarm import G90Alarm, SensorStateCallback, SensorLowBatteryCallback
|
|
30
36
|
|
|
31
|
-
# Common protocol fields across read and write operations
|
|
32
|
-
COMMON_FIELDS = [
|
|
33
|
-
'parent_name',
|
|
34
|
-
'index',
|
|
35
|
-
'room_id',
|
|
36
|
-
'type_id',
|
|
37
|
-
'subtype',
|
|
38
|
-
'timeout',
|
|
39
|
-
'user_flag_data',
|
|
40
|
-
'baudrate',
|
|
41
|
-
'protocol_id',
|
|
42
|
-
'reserved_data',
|
|
43
|
-
'node_count',
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
# Incoming (read operation) protocol fields
|
|
47
|
-
INCOMING_FIELDS = COMMON_FIELDS + [
|
|
48
|
-
'mask',
|
|
49
|
-
'private_data',
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
# Outgoing (write operation) protocol fields
|
|
53
|
-
OUTGOING_FIELDS = COMMON_FIELDS + [
|
|
54
|
-
'rx',
|
|
55
|
-
'tx',
|
|
56
|
-
'private_data',
|
|
57
|
-
]
|
|
58
37
|
|
|
38
|
+
@dataclass
|
|
39
|
+
class G90SensorCommonData: # pylint:disable=too-many-instance-attributes
|
|
40
|
+
"""
|
|
41
|
+
Common protocol fields across read and write operations.
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
:meta private:
|
|
61
44
|
"""
|
|
62
|
-
|
|
45
|
+
parent_name: str
|
|
46
|
+
index: int
|
|
47
|
+
room_id: int
|
|
48
|
+
type_id: int
|
|
49
|
+
subtype: int
|
|
50
|
+
timeout: int
|
|
51
|
+
user_flag_data: int
|
|
52
|
+
baudrate: int
|
|
53
|
+
protocol_id: int
|
|
54
|
+
reserved_data: int
|
|
55
|
+
node_count: int
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class G90SensorIncomingData(G90SensorCommonData):
|
|
60
|
+
"""
|
|
61
|
+
Incoming (read operation) protocol fields.
|
|
63
62
|
|
|
64
63
|
:meta private:
|
|
65
64
|
"""
|
|
65
|
+
mask: int
|
|
66
|
+
private_data: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class G90SensorOutgoingData(G90SensorCommonData):
|
|
71
|
+
"""
|
|
72
|
+
Outgoing (write operation) protocol fields.
|
|
73
|
+
|
|
74
|
+
:meta private:
|
|
75
|
+
"""
|
|
76
|
+
rx: int # pylint:disable=invalid-name
|
|
77
|
+
tx: int # pylint:disable=invalid-name
|
|
78
|
+
private_data: str
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class G90SensorReservedFlags(IntFlag):
|
|
82
|
+
"""
|
|
83
|
+
Reserved flags of the sensor.
|
|
84
|
+
"""
|
|
66
85
|
NONE = 0
|
|
67
86
|
CAN_READ = 16
|
|
68
87
|
CAN_READ_EXT = 32
|
|
@@ -72,8 +91,6 @@ class G90SensorReservedFlags(IntFlag):
|
|
|
72
91
|
class G90SensorUserFlags(IntFlag):
|
|
73
92
|
"""
|
|
74
93
|
User flags of the sensor.
|
|
75
|
-
|
|
76
|
-
:meta private:
|
|
77
94
|
"""
|
|
78
95
|
NONE = 0
|
|
79
96
|
ENABLED = 1
|
|
@@ -89,8 +106,6 @@ class G90SensorUserFlags(IntFlag):
|
|
|
89
106
|
class G90SensorProtocols(IntEnum):
|
|
90
107
|
"""
|
|
91
108
|
Protocol types for the sensors.
|
|
92
|
-
|
|
93
|
-
:meta private:
|
|
94
109
|
"""
|
|
95
110
|
RF_1527 = 0
|
|
96
111
|
RF_2262 = 1
|
|
@@ -104,8 +119,6 @@ class G90SensorProtocols(IntEnum):
|
|
|
104
119
|
class G90SensorTypes(IntEnum):
|
|
105
120
|
"""
|
|
106
121
|
Sensor types.
|
|
107
|
-
|
|
108
|
-
:meta private:
|
|
109
122
|
"""
|
|
110
123
|
DOOR = 1
|
|
111
124
|
GLASS = 2
|
|
@@ -160,23 +173,23 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
160
173
|
:param kwargs: Pass-through keyword arguments for for interpreting protocol
|
|
161
174
|
fields
|
|
162
175
|
"""
|
|
163
|
-
def __init__(
|
|
164
|
-
self
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
self.
|
|
168
|
-
|
|
169
|
-
)
|
|
176
|
+
def __init__(
|
|
177
|
+
self, *args: Any, parent: G90Alarm, subindex: int, proto_idx: int,
|
|
178
|
+
**kwargs: Any
|
|
179
|
+
) -> None:
|
|
180
|
+
self._protocol_incoming_data_kls = G90SensorIncomingData
|
|
181
|
+
self._protocol_outgoing_data_kls = G90SensorOutgoingData
|
|
170
182
|
self._protocol_data = self._protocol_incoming_data_kls(*args, **kwargs)
|
|
171
183
|
self._parent = parent
|
|
172
184
|
self._subindex = subindex
|
|
173
185
|
self._occupancy = False
|
|
174
|
-
self._state_callback = None
|
|
175
|
-
self._low_battery_callback = None
|
|
186
|
+
self._state_callback: Optional[SensorStateCallback] = None
|
|
187
|
+
self._low_battery_callback: Optional[SensorLowBatteryCallback] = None
|
|
188
|
+
self._low_battery = False
|
|
176
189
|
self._proto_idx = proto_idx
|
|
177
|
-
self._extra_data = None
|
|
190
|
+
self._extra_data: Any = None
|
|
178
191
|
|
|
179
|
-
self._definition = None
|
|
192
|
+
self._definition: Optional[SensorDefinition] = None
|
|
180
193
|
# Get sensor definition corresponds to the sensor type/subtype if any
|
|
181
194
|
for s_def in SENSOR_DEFINITIONS:
|
|
182
195
|
if (
|
|
@@ -187,184 +200,208 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
187
200
|
break
|
|
188
201
|
|
|
189
202
|
@property
|
|
190
|
-
def name(self):
|
|
203
|
+
def name(self) -> str:
|
|
191
204
|
"""
|
|
192
|
-
|
|
205
|
+
Sensor name, accounting for multi-channel entities (single
|
|
193
206
|
protocol entity results in multiple :class:`.G90Sensor` instances).
|
|
194
207
|
|
|
195
208
|
:return: Sensor name
|
|
196
|
-
:rtype: str
|
|
197
209
|
"""
|
|
198
210
|
if self._protocol_data.node_count == 1:
|
|
199
211
|
return self._protocol_data.parent_name
|
|
200
212
|
return f'{self._protocol_data.parent_name}#{self._subindex + 1}'
|
|
201
213
|
|
|
202
214
|
@property
|
|
203
|
-
def state_callback(self):
|
|
215
|
+
def state_callback(self) -> Optional[SensorStateCallback]:
|
|
204
216
|
"""
|
|
205
|
-
|
|
217
|
+
Callback that is invoked when the sensor changes its state.
|
|
206
218
|
|
|
207
219
|
:return: Sensor state callback
|
|
208
|
-
:rtype: object
|
|
209
220
|
"""
|
|
210
221
|
return self._state_callback
|
|
211
222
|
|
|
212
223
|
@state_callback.setter
|
|
213
|
-
def state_callback(self, value):
|
|
214
|
-
"""
|
|
215
|
-
Sets callback for the state changes of the sensor.
|
|
216
|
-
|
|
217
|
-
:param object value: Sensor state callback
|
|
218
|
-
"""
|
|
224
|
+
def state_callback(self, value: SensorStateCallback) -> None:
|
|
219
225
|
self._state_callback = value
|
|
220
226
|
|
|
221
227
|
@property
|
|
222
|
-
def low_battery_callback(self):
|
|
228
|
+
def low_battery_callback(self) -> Optional[SensorLowBatteryCallback]:
|
|
223
229
|
"""
|
|
224
|
-
|
|
230
|
+
Callback that is invoked when the sensor reports on low battery
|
|
231
|
+
condition.
|
|
225
232
|
|
|
226
233
|
:return: Sensor's low battery callback
|
|
227
|
-
:rtype: object
|
|
228
234
|
"""
|
|
229
235
|
return self._low_battery_callback
|
|
230
236
|
|
|
231
237
|
@low_battery_callback.setter
|
|
232
|
-
def low_battery_callback(self, value):
|
|
233
|
-
"""
|
|
234
|
-
Sets callback for the low battery condition reported by the sensor.
|
|
235
|
-
|
|
236
|
-
:param object value: Sensor's low battery callback
|
|
237
|
-
"""
|
|
238
|
+
def low_battery_callback(self, value: SensorLowBatteryCallback) -> None:
|
|
238
239
|
self._low_battery_callback = value
|
|
239
240
|
|
|
240
241
|
@property
|
|
241
|
-
def occupancy(self):
|
|
242
|
+
def occupancy(self) -> bool:
|
|
242
243
|
"""
|
|
243
244
|
Occupancy (occupied/not occupied, or triggered/not triggered)
|
|
244
245
|
for the sensor.
|
|
245
246
|
|
|
246
247
|
:return: Sensor occupancy
|
|
247
|
-
:rtype: bool
|
|
248
248
|
"""
|
|
249
249
|
return self._occupancy
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
def occupancy(self, value):
|
|
251
|
+
def _set_occupancy(self, value: bool) -> None:
|
|
253
252
|
"""
|
|
254
|
-
Sets occupancy state
|
|
253
|
+
Sets occupancy state of the sensor.
|
|
254
|
+
Intentionally private, as occupancy state is derived from
|
|
255
|
+
notifications/alerts.
|
|
255
256
|
|
|
256
|
-
:param
|
|
257
|
+
:param value: Occupancy state
|
|
257
258
|
"""
|
|
259
|
+
_LOGGER.debug(
|
|
260
|
+
"Setting occupancy for sensor index=%s: '%s' %s"
|
|
261
|
+
" (previous value: %s)",
|
|
262
|
+
self.index, self.name, value, self._occupancy
|
|
263
|
+
)
|
|
258
264
|
self._occupancy = value
|
|
259
265
|
|
|
260
266
|
@property
|
|
261
|
-
def protocol(self):
|
|
267
|
+
def protocol(self) -> G90SensorProtocols:
|
|
262
268
|
"""
|
|
263
|
-
|
|
269
|
+
Protocol type of the sensor.
|
|
264
270
|
|
|
265
271
|
:return: Protocol type
|
|
266
|
-
:rtype: int
|
|
267
272
|
"""
|
|
268
273
|
return G90SensorProtocols(self._protocol_data.protocol_id)
|
|
269
274
|
|
|
270
275
|
@property
|
|
271
|
-
def type(self):
|
|
276
|
+
def type(self) -> G90SensorTypes:
|
|
272
277
|
"""
|
|
273
|
-
|
|
278
|
+
Type of the sensor.
|
|
274
279
|
|
|
275
280
|
:return: Sensor type
|
|
276
|
-
:rtype: int
|
|
277
281
|
"""
|
|
278
282
|
return G90SensorTypes(self._protocol_data.type_id)
|
|
279
283
|
|
|
280
284
|
@property
|
|
281
|
-
def
|
|
285
|
+
def subtype(self) -> int:
|
|
286
|
+
"""
|
|
287
|
+
Sub-type of the sensor.
|
|
288
|
+
|
|
289
|
+
:return: Sensor sub-type
|
|
282
290
|
"""
|
|
283
|
-
|
|
291
|
+
return self._protocol_data.subtype
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def reserved(self) -> G90SensorReservedFlags:
|
|
295
|
+
"""
|
|
296
|
+
Reserved flags (read/write mode) for the sensor.
|
|
284
297
|
|
|
285
298
|
:return: Reserved flags
|
|
286
|
-
:rtype: int
|
|
287
299
|
"""
|
|
288
300
|
return G90SensorReservedFlags(self._protocol_data.reserved_data)
|
|
289
301
|
|
|
290
302
|
@property
|
|
291
|
-
def user_flag(self):
|
|
303
|
+
def user_flag(self) -> G90SensorUserFlags:
|
|
292
304
|
"""
|
|
293
|
-
|
|
305
|
+
User flags for the sensor (disabled/enabled, arming type etc).
|
|
294
306
|
|
|
295
307
|
:return: User flags
|
|
296
|
-
:rtype: int
|
|
297
308
|
"""
|
|
298
309
|
return G90SensorUserFlags(self._protocol_data.user_flag_data)
|
|
299
310
|
|
|
300
311
|
@property
|
|
301
|
-
def node_count(self):
|
|
312
|
+
def node_count(self) -> int:
|
|
302
313
|
"""
|
|
303
|
-
|
|
314
|
+
Number of nodes (channels) for the sensor.
|
|
304
315
|
|
|
305
316
|
:return: Number of nodes
|
|
306
|
-
:rtype: int
|
|
307
317
|
"""
|
|
308
318
|
return self._protocol_data.node_count
|
|
309
319
|
|
|
310
320
|
@property
|
|
311
|
-
def parent(self):
|
|
321
|
+
def parent(self) -> G90Alarm:
|
|
312
322
|
"""
|
|
313
|
-
|
|
323
|
+
Parent instance of alarm panel class the sensor is associated
|
|
314
324
|
with.
|
|
315
325
|
|
|
316
|
-
:return: Parent
|
|
317
|
-
:rtype: :class:`.G90Alarm`
|
|
326
|
+
:return: Parent instance
|
|
318
327
|
"""
|
|
319
328
|
return self._parent
|
|
320
329
|
|
|
321
330
|
@property
|
|
322
|
-
def index(self):
|
|
331
|
+
def index(self) -> int:
|
|
323
332
|
"""
|
|
324
|
-
|
|
333
|
+
Index (internal position) of the sensor in the alarm panel.
|
|
325
334
|
|
|
326
335
|
:return: Internal sensor position
|
|
327
|
-
:rtype: int
|
|
328
336
|
"""
|
|
329
337
|
return self._protocol_data.index
|
|
330
338
|
|
|
331
339
|
@property
|
|
332
|
-
def subindex(self):
|
|
340
|
+
def subindex(self) -> int:
|
|
333
341
|
"""
|
|
334
|
-
|
|
342
|
+
Index of the sensor within multi-node device.
|
|
335
343
|
|
|
336
344
|
:return: Index of sensor in multi-node device.
|
|
337
|
-
:rtype: int
|
|
338
345
|
"""
|
|
339
346
|
return self._subindex
|
|
340
347
|
|
|
341
348
|
@property
|
|
342
|
-
def supports_enable_disable(self):
|
|
349
|
+
def supports_enable_disable(self) -> bool:
|
|
343
350
|
"""
|
|
344
351
|
Indicates if disabling/enabling the sensor is supported.
|
|
345
352
|
|
|
346
353
|
:return: Support for enabling/disabling the sensor
|
|
347
|
-
:rtype: bool
|
|
348
354
|
"""
|
|
349
355
|
return self._definition is not None
|
|
350
356
|
|
|
351
357
|
@property
|
|
352
|
-
def
|
|
358
|
+
def is_wireless(self) -> bool:
|
|
359
|
+
"""
|
|
360
|
+
Indicates if the sensor is wireless.
|
|
361
|
+
"""
|
|
362
|
+
return self.protocol not in (G90SensorProtocols.CORD,)
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def is_low_battery(self) -> bool:
|
|
366
|
+
"""
|
|
367
|
+
Indicates if the sensor is reporting low battery.
|
|
368
|
+
"""
|
|
369
|
+
return self._low_battery
|
|
370
|
+
|
|
371
|
+
def _set_low_battery(self, value: bool) -> None:
|
|
372
|
+
"""
|
|
373
|
+
Sets low battery state of the sensor.
|
|
374
|
+
Intentionally private, as low battery state is derived from
|
|
375
|
+
notifications/alerts.
|
|
376
|
+
|
|
377
|
+
:param value: Low battery state
|
|
378
|
+
"""
|
|
379
|
+
_LOGGER.debug(
|
|
380
|
+
"Setting low battery for sensor index=%s '%s': %s"
|
|
381
|
+
" (previous value: %s)",
|
|
382
|
+
self.index, self.name, value, self._low_battery
|
|
383
|
+
)
|
|
384
|
+
self._low_battery = value
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def enabled(self) -> bool:
|
|
353
388
|
"""
|
|
354
389
|
Indicates if the sensor is enabled.
|
|
355
390
|
|
|
356
391
|
:return: If sensor is enabled
|
|
357
|
-
:rtype: bool
|
|
358
392
|
"""
|
|
359
|
-
return self.user_flag & G90SensorUserFlags.ENABLED
|
|
393
|
+
return self.user_flag & G90SensorUserFlags.ENABLED != 0
|
|
360
394
|
|
|
361
|
-
async def set_enabled(self, value):
|
|
395
|
+
async def set_enabled(self, value: bool) -> None:
|
|
362
396
|
"""
|
|
363
397
|
Sets disabled/enabled state of the sensor.
|
|
364
398
|
|
|
365
|
-
:param
|
|
399
|
+
:param value: Whether to enable or disable the sensor
|
|
366
400
|
"""
|
|
367
|
-
|
|
401
|
+
# Checking private attribute directly, since `mypy` doesn't recognize
|
|
402
|
+
# the check for sensor definition to be defined if done over
|
|
403
|
+
# `self.supports_enable_disable` property
|
|
404
|
+
if not self._definition:
|
|
368
405
|
_LOGGER.warning(
|
|
369
406
|
'Manipulating with enable/disable for sensor index=%s'
|
|
370
407
|
' is unsupported - no sensor definition for'
|
|
@@ -388,7 +425,7 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
388
425
|
G90Commands.GETSENSORLIST,
|
|
389
426
|
start=self._proto_idx, end=self._proto_idx
|
|
390
427
|
)
|
|
391
|
-
sensors = [x async for x in sensors_result]
|
|
428
|
+
sensors = [x.data async for x in sensors_result]
|
|
392
429
|
|
|
393
430
|
# Abort if sensor is not found
|
|
394
431
|
if not sensors:
|
|
@@ -401,7 +438,7 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
401
438
|
|
|
402
439
|
# Compare actual sensor data from what the sensor has been instantiated
|
|
403
440
|
# from, and abort the operation if out-of-band changes are detected.
|
|
404
|
-
|
|
441
|
+
sensor_data = sensors[0]
|
|
405
442
|
if self._protocol_incoming_data_kls(
|
|
406
443
|
*sensor_data
|
|
407
444
|
) != self._protocol_data:
|
|
@@ -422,7 +459,7 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
422
459
|
user_flag &= ~G90SensorUserFlags.ENABLED
|
|
423
460
|
|
|
424
461
|
# Re-instantiate the protocol data with modified user flags
|
|
425
|
-
_data = self._protocol_data
|
|
462
|
+
_data = asdict(self._protocol_data)
|
|
426
463
|
_data['user_flag_data'] = user_flag
|
|
427
464
|
self._protocol_data = self._protocol_incoming_data_kls(**_data)
|
|
428
465
|
|
|
@@ -454,32 +491,49 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
|
|
|
454
491
|
)
|
|
455
492
|
# Modify the sensor
|
|
456
493
|
await self._parent.command(
|
|
457
|
-
G90Commands.SETSINGLESENSOR, list(outgoing_data)
|
|
494
|
+
G90Commands.SETSINGLESENSOR, list(astuple(outgoing_data))
|
|
458
495
|
)
|
|
459
496
|
|
|
460
497
|
@property
|
|
461
|
-
def extra_data(self):
|
|
498
|
+
def extra_data(self) -> Any:
|
|
462
499
|
"""
|
|
463
|
-
|
|
500
|
+
Extra data for the sensor, that can be used to store
|
|
464
501
|
caller-specific information and will be carried by the sensor instance.
|
|
465
502
|
"""
|
|
466
503
|
return self._extra_data
|
|
467
504
|
|
|
468
505
|
@extra_data.setter
|
|
469
|
-
def extra_data(self, val):
|
|
506
|
+
def extra_data(self, val: Any) -> None:
|
|
470
507
|
self._extra_data = val
|
|
471
508
|
|
|
472
|
-
def
|
|
509
|
+
def _asdict(self) -> Dict[str, Any]:
|
|
510
|
+
"""
|
|
511
|
+
Returns dictionary representation of the sensor.
|
|
512
|
+
|
|
513
|
+
:return: Dictionary representation
|
|
514
|
+
"""
|
|
515
|
+
return {
|
|
516
|
+
'name': self.name,
|
|
517
|
+
'type': self.type,
|
|
518
|
+
'subtype': self.subtype,
|
|
519
|
+
'index': self.index,
|
|
520
|
+
'subindex': self.subindex,
|
|
521
|
+
'node_count': self.node_count,
|
|
522
|
+
'protocol': self.protocol,
|
|
523
|
+
'occupancy': self.occupancy,
|
|
524
|
+
'user_flag': self.user_flag,
|
|
525
|
+
'reserved': self.reserved,
|
|
526
|
+
'extra_data': self.extra_data,
|
|
527
|
+
'enabled': self.enabled,
|
|
528
|
+
'supports_enable_disable': self.supports_enable_disable,
|
|
529
|
+
'is_wireless': self.is_wireless,
|
|
530
|
+
'is_low_battery': self.is_low_battery,
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
def __repr__(self) -> str:
|
|
473
534
|
"""
|
|
474
535
|
Returns string representation of the sensor.
|
|
475
536
|
|
|
476
537
|
:return: String representation
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return super().__repr__() + f"(name='{str(self.name)}'" \
|
|
480
|
-
f' type={str(self.type)}' \
|
|
481
|
-
f' protocol={str(self.protocol)}' \
|
|
482
|
-
f' occupancy={self.occupancy}' \
|
|
483
|
-
f' user flag={str(self.user_flag)}' \
|
|
484
|
-
f' reserved={str(self.reserved)}' \
|
|
485
|
-
f" extra_data={str(self.extra_data)})"
|
|
538
|
+
"""
|
|
539
|
+
return super().__repr__() + f'({repr(self._asdict())})'
|
pyg90alarm/history.py
CHANGED
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"""
|
|
22
22
|
History protocol entity.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from typing import Any, Optional, Dict
|
|
25
|
+
from dataclasses import dataclass
|
|
25
26
|
from datetime import datetime, timezone
|
|
26
|
-
from collections import namedtuple
|
|
27
27
|
from .const import (
|
|
28
28
|
G90AlertTypes,
|
|
29
29
|
G90AlertSources,
|
|
@@ -65,52 +65,50 @@ states_mapping = {
|
|
|
65
65
|
G90HistoryStates.WIFI_DISCONNECTED,
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ProtocolData:
|
|
71
|
+
"""
|
|
72
|
+
Class representing the data incoming from the device
|
|
73
|
+
|
|
74
|
+
:meta private:
|
|
75
|
+
"""
|
|
76
|
+
type: G90AlertTypes
|
|
77
|
+
event_id: G90AlertStateChangeTypes
|
|
78
|
+
source: G90AlertSources
|
|
79
|
+
state: int
|
|
80
|
+
sensor_name: str
|
|
81
|
+
unix_time: int
|
|
82
|
+
other: str
|
|
79
83
|
|
|
80
84
|
|
|
81
85
|
class G90History:
|
|
82
86
|
"""
|
|
83
|
-
|
|
87
|
+
Represents a history entry from the alarm panel.
|
|
84
88
|
"""
|
|
85
|
-
def __init__(self, *args, **kwargs):
|
|
89
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
86
90
|
self._protocol_data = ProtocolData(*args, **kwargs)
|
|
87
91
|
|
|
88
92
|
@property
|
|
89
|
-
def datetime(self):
|
|
93
|
+
def datetime(self) -> datetime:
|
|
90
94
|
"""
|
|
91
95
|
Date/time of the history entry.
|
|
92
|
-
|
|
93
|
-
:rtype: :class:`datetime.datetime`
|
|
94
96
|
"""
|
|
95
97
|
return datetime.fromtimestamp(
|
|
96
98
|
self._protocol_data.unix_time, tz=timezone.utc
|
|
97
99
|
)
|
|
98
100
|
|
|
99
101
|
@property
|
|
100
|
-
def type(self):
|
|
102
|
+
def type(self) -> G90AlertTypes:
|
|
101
103
|
"""
|
|
102
104
|
Type of the history entry.
|
|
103
|
-
|
|
104
|
-
:rtype: :class:`.G90AlertTypes`
|
|
105
105
|
"""
|
|
106
106
|
return G90AlertTypes(self._protocol_data.type)
|
|
107
107
|
|
|
108
108
|
@property
|
|
109
|
-
def state(self):
|
|
109
|
+
def state(self) -> G90HistoryStates:
|
|
110
110
|
"""
|
|
111
111
|
State for the history entry.
|
|
112
|
-
|
|
113
|
-
:rtype: :class:`.G90HistoryStates`
|
|
114
112
|
"""
|
|
115
113
|
# Door open/close type, mapped against `G90AlertStates` using `state`
|
|
116
114
|
# incoming field
|
|
@@ -140,11 +138,9 @@ class G90History:
|
|
|
140
138
|
)
|
|
141
139
|
|
|
142
140
|
@property
|
|
143
|
-
def source(self):
|
|
141
|
+
def source(self) -> G90AlertSources:
|
|
144
142
|
"""
|
|
145
143
|
Source of the history entry.
|
|
146
|
-
|
|
147
|
-
:rtype: :class:`.G90AlertSources`
|
|
148
144
|
"""
|
|
149
145
|
# Device state changes or open/close events are mapped against
|
|
150
146
|
# `G90AlertSources` using `source` incoming field
|
|
@@ -162,22 +158,18 @@ class G90History:
|
|
|
162
158
|
return G90AlertSources.DEVICE
|
|
163
159
|
|
|
164
160
|
@property
|
|
165
|
-
def sensor_name(self):
|
|
161
|
+
def sensor_name(self) -> Optional[str]:
|
|
166
162
|
"""
|
|
167
163
|
Name of the sensor related to the history entry, might be empty if none
|
|
168
164
|
associated.
|
|
169
|
-
|
|
170
|
-
:rtype: str|None
|
|
171
165
|
"""
|
|
172
166
|
return self._protocol_data.sensor_name or None
|
|
173
167
|
|
|
174
168
|
@property
|
|
175
|
-
def sensor_idx(self):
|
|
169
|
+
def sensor_idx(self) -> Optional[int]:
|
|
176
170
|
"""
|
|
177
171
|
ID of the sensor related to the history entry, might be empty if none
|
|
178
172
|
associated.
|
|
179
|
-
|
|
180
|
-
:rtype: str|None
|
|
181
173
|
"""
|
|
182
174
|
# Sensor ID will only be available if entry source is a sensor
|
|
183
175
|
if self.source == G90AlertSources.SENSOR:
|
|
@@ -185,12 +177,10 @@ class G90History:
|
|
|
185
177
|
|
|
186
178
|
return None
|
|
187
179
|
|
|
188
|
-
def as_device_alert(self):
|
|
180
|
+
def as_device_alert(self) -> G90DeviceAlert:
|
|
189
181
|
"""
|
|
190
182
|
Returns the history entry represented as device alert structure,
|
|
191
183
|
suitable for :meth:`G90DeviceNotifications._handle_alert`.
|
|
192
|
-
|
|
193
|
-
:rtype: :class:`.G90DeviceAlert`
|
|
194
184
|
"""
|
|
195
185
|
return G90DeviceAlert(
|
|
196
186
|
type=self._protocol_data.type,
|
|
@@ -198,21 +188,27 @@ class G90History:
|
|
|
198
188
|
source=self._protocol_data.source,
|
|
199
189
|
state=self._protocol_data.state,
|
|
200
190
|
zone_name=self._protocol_data.sensor_name,
|
|
201
|
-
device_id=
|
|
191
|
+
device_id='',
|
|
202
192
|
unix_time=self._protocol_data.unix_time,
|
|
203
|
-
resv4=
|
|
193
|
+
resv4=0,
|
|
204
194
|
other=self._protocol_data.other
|
|
205
195
|
)
|
|
206
196
|
|
|
207
|
-
def
|
|
197
|
+
def _asdict(self) -> Dict[str, Any]:
|
|
208
198
|
"""
|
|
209
|
-
|
|
199
|
+
Returns the history entry as dictionary.
|
|
200
|
+
"""
|
|
201
|
+
return {
|
|
202
|
+
'type': self.type,
|
|
203
|
+
'source': self.source,
|
|
204
|
+
'state': self.state,
|
|
205
|
+
'sensor_name': self.sensor_name,
|
|
206
|
+
'sensor_idx': self.sensor_idx,
|
|
207
|
+
'datetime': self.datetime,
|
|
208
|
+
}
|
|
210
209
|
|
|
211
|
-
|
|
210
|
+
def __repr__(self) -> str:
|
|
211
|
+
"""
|
|
212
|
+
Textural representation of the history entry.
|
|
212
213
|
"""
|
|
213
|
-
return f'
|
|
214
|
-
+ f' source={repr(self.source)}' \
|
|
215
|
-
+ f' state={repr(self.state)}' \
|
|
216
|
-
+ f' sensor_name={self.sensor_name}' \
|
|
217
|
-
+ f' sensor_idx={self.sensor_idx}' \
|
|
218
|
-
+ f' datetime={repr(self.datetime)}'
|
|
214
|
+
return super().__repr__() + f'({repr(self._asdict())})'
|