pyg90alarm 1.13.0__py3-none-any.whl → 1.14.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 +185 -161
- 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 +138 -120
- pyg90alarm/history.py +42 -41
- 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.14.0.dist-info}/METADATA +4 -3
- pyg90alarm-1.14.0.dist-info/RECORD +27 -0
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.14.0.dist-info}/WHEEL +1 -1
- pyg90alarm-1.13.0.dist-info/RECORD +0 -26
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.14.0.dist-info}/LICENSE +0 -0
- {pyg90alarm-1.13.0.dist-info → pyg90alarm-1.14.0.dist-info}/top_level.txt +0 -0
|
@@ -23,8 +23,13 @@ Implements support for notifications/alerts sent by G90 alarm panel.
|
|
|
23
23
|
"""
|
|
24
24
|
import json
|
|
25
25
|
import logging
|
|
26
|
-
from
|
|
26
|
+
from typing import (
|
|
27
|
+
Optional, Tuple, List, Any
|
|
28
|
+
)
|
|
29
|
+
from dataclasses import dataclass
|
|
27
30
|
import asyncio
|
|
31
|
+
from asyncio.transports import BaseTransport
|
|
32
|
+
from asyncio.protocols import DatagramProtocol
|
|
28
33
|
from .callback import G90Callback
|
|
29
34
|
from .const import (
|
|
30
35
|
G90MessageTypes,
|
|
@@ -40,64 +45,78 @@ from .const import (
|
|
|
40
45
|
_LOGGER = logging.getLogger(__name__)
|
|
41
46
|
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
@dataclass
|
|
49
|
+
class G90Message:
|
|
45
50
|
"""
|
|
46
51
|
Represents the message received from the device.
|
|
47
52
|
|
|
48
53
|
:meta private:
|
|
49
54
|
"""
|
|
55
|
+
code: G90MessageTypes
|
|
56
|
+
data: List[Any]
|
|
50
57
|
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
@dataclass
|
|
60
|
+
class G90Notification:
|
|
54
61
|
"""
|
|
55
62
|
Represents the notification received from the device.
|
|
56
63
|
|
|
57
64
|
:meta private:
|
|
58
65
|
"""
|
|
66
|
+
kind: G90NotificationTypes
|
|
67
|
+
data: List[Any]
|
|
59
68
|
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
@dataclass
|
|
71
|
+
class G90ZoneInfo:
|
|
63
72
|
"""
|
|
64
73
|
Represents zone details received from the device.
|
|
65
74
|
|
|
66
75
|
:meta private:
|
|
67
76
|
"""
|
|
77
|
+
idx: int
|
|
78
|
+
name: str
|
|
68
79
|
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
@dataclass
|
|
82
|
+
class G90ArmDisarmInfo:
|
|
72
83
|
"""
|
|
73
84
|
Represents the arm/disarm state received from the device.
|
|
74
85
|
|
|
75
86
|
:meta private:
|
|
76
87
|
"""
|
|
88
|
+
state: int
|
|
77
89
|
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'zone_name', 'device_id', 'unix_time',
|
|
82
|
-
'resv4', 'other'])):
|
|
91
|
+
@dataclass
|
|
92
|
+
class G90DeviceAlert: # pylint: disable=too-many-instance-attributes
|
|
83
93
|
"""
|
|
84
94
|
Represents alert received from the device.
|
|
85
|
-
|
|
86
|
-
:meta private:
|
|
87
95
|
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
type: G90AlertTypes
|
|
97
|
+
event_id: G90AlertStateChangeTypes
|
|
98
|
+
source: G90AlertSources
|
|
99
|
+
state: int
|
|
100
|
+
zone_name: str
|
|
101
|
+
device_id: str
|
|
102
|
+
unix_time: int
|
|
103
|
+
resv4: int
|
|
104
|
+
other: str
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class G90DeviceNotifications(DatagramProtocol):
|
|
91
108
|
"""
|
|
92
|
-
|
|
109
|
+
Implements support for notifications/alerts sent by alarm panel.
|
|
93
110
|
"""
|
|
94
|
-
def __init__(self, port, host):
|
|
111
|
+
def __init__(self, port: int, host: str):
|
|
95
112
|
# pylint: disable=too-many-arguments
|
|
96
|
-
self._notification_transport = None
|
|
113
|
+
self._notification_transport: Optional[BaseTransport] = None
|
|
97
114
|
self._notifications_host = host
|
|
98
115
|
self._notifications_port = port
|
|
99
116
|
|
|
100
|
-
def _handle_notification(
|
|
117
|
+
def _handle_notification(
|
|
118
|
+
self, addr: Tuple[str, int], notification: G90Notification
|
|
119
|
+
) -> None:
|
|
101
120
|
# Sensor activity notification
|
|
102
121
|
if notification.kind == G90NotificationTypes.SENSOR_ACTIVITY:
|
|
103
122
|
g90_zone_info = G90ZoneInfo(*notification.data)
|
|
@@ -123,7 +142,9 @@ class G90DeviceNotifications:
|
|
|
123
142
|
' kind %s, data %s',
|
|
124
143
|
addr[0], addr[1], notification.kind, notification.data)
|
|
125
144
|
|
|
126
|
-
def _handle_alert(
|
|
145
|
+
def _handle_alert(
|
|
146
|
+
self, addr: Tuple[str, int], alert: G90DeviceAlert
|
|
147
|
+
) -> None:
|
|
127
148
|
if alert.type == G90AlertTypes.DOOR_OPEN_CLOSE:
|
|
128
149
|
if alert.state in (
|
|
129
150
|
G90AlertStates.DOOR_OPEN, G90AlertStates.DOOR_CLOSE
|
|
@@ -163,7 +184,7 @@ class G90DeviceNotifications:
|
|
|
163
184
|
G90AlertStateChangeTypes.DISARM: G90ArmDisarmTypes.DISARM
|
|
164
185
|
}
|
|
165
186
|
|
|
166
|
-
state = alarm_arm_disarm_state_map.get(alert.event_id
|
|
187
|
+
state = alarm_arm_disarm_state_map.get(alert.event_id)
|
|
167
188
|
if state:
|
|
168
189
|
# We received the device state change related to arm/disarm,
|
|
169
190
|
# invoke the corresponding callback
|
|
@@ -185,17 +206,20 @@ class G90DeviceNotifications:
|
|
|
185
206
|
|
|
186
207
|
# Implementation of datagram protocol,
|
|
187
208
|
# https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols
|
|
188
|
-
def connection_made(self, transport):
|
|
209
|
+
def connection_made(self, transport: BaseTransport) -> None:
|
|
189
210
|
"""
|
|
190
211
|
Invoked when connection from the device is made.
|
|
191
212
|
"""
|
|
192
213
|
|
|
193
|
-
def connection_lost(self, exc):
|
|
214
|
+
def connection_lost(self, exc: Optional[Exception]) -> None:
|
|
194
215
|
"""
|
|
195
216
|
Same but when the connection is lost.
|
|
196
217
|
"""
|
|
197
218
|
|
|
198
|
-
def datagram_received(
|
|
219
|
+
def datagram_received( # pylint:disable=R0911
|
|
220
|
+
self, data: bytes, addr: Tuple[str, int]
|
|
221
|
+
) -> None:
|
|
222
|
+
|
|
199
223
|
"""
|
|
200
224
|
Invoked from datagram is received from the device.
|
|
201
225
|
"""
|
|
@@ -221,54 +245,56 @@ class G90DeviceNotifications:
|
|
|
221
245
|
# Device notifications
|
|
222
246
|
if g90_message.code == G90MessageTypes.NOTIFICATION:
|
|
223
247
|
try:
|
|
224
|
-
|
|
248
|
+
notification_data = G90Notification(*g90_message.data)
|
|
225
249
|
except TypeError as exc:
|
|
226
250
|
_LOGGER.error('Bad notification received from %s:%s: %s',
|
|
227
251
|
addr[0], addr[1], exc)
|
|
228
252
|
return
|
|
229
|
-
self._handle_notification(addr,
|
|
253
|
+
self._handle_notification(addr, notification_data)
|
|
230
254
|
return
|
|
231
255
|
|
|
232
256
|
# Device alerts
|
|
233
257
|
if g90_message.code == G90MessageTypes.ALERT:
|
|
234
258
|
try:
|
|
235
|
-
|
|
259
|
+
alert_data = G90DeviceAlert(*g90_message.data)
|
|
236
260
|
except TypeError as exc:
|
|
237
261
|
_LOGGER.error('Bad alert received from %s:%s: %s',
|
|
238
262
|
addr[0], addr[1], exc)
|
|
239
263
|
return
|
|
240
|
-
self._handle_alert(addr,
|
|
264
|
+
self._handle_alert(addr, alert_data)
|
|
241
265
|
return
|
|
242
266
|
|
|
243
267
|
_LOGGER.warning('Unknown message received from %s:%s: %s',
|
|
244
268
|
addr[0], addr[1], message)
|
|
245
269
|
|
|
246
|
-
async def on_armdisarm(self, state):
|
|
270
|
+
async def on_armdisarm(self, state: G90ArmDisarmTypes) -> None:
|
|
247
271
|
"""
|
|
248
272
|
Invoked when device is armed or disarmed.
|
|
249
273
|
"""
|
|
250
274
|
|
|
251
|
-
async def on_sensor_activity(self, idx, name):
|
|
275
|
+
async def on_sensor_activity(self, idx: int, name: str) -> None:
|
|
252
276
|
"""
|
|
253
277
|
Invoked on sensor activity.
|
|
254
278
|
"""
|
|
255
279
|
|
|
256
|
-
async def on_door_open_close(
|
|
280
|
+
async def on_door_open_close(
|
|
281
|
+
self, event_id: int, zone_name: str, is_open: bool
|
|
282
|
+
) -> None:
|
|
257
283
|
"""
|
|
258
284
|
Invoked when door sensor reports it opened or closed.
|
|
259
285
|
"""
|
|
260
286
|
|
|
261
|
-
async def on_low_battery(self, event_id, zone_name):
|
|
287
|
+
async def on_low_battery(self, event_id: int, zone_name: str) -> None:
|
|
262
288
|
"""
|
|
263
289
|
Invoked when a sensor reports it is low on battery.
|
|
264
290
|
"""
|
|
265
291
|
|
|
266
|
-
async def on_alarm(self, event_id, zone_name):
|
|
292
|
+
async def on_alarm(self, event_id: int, zone_name: str) -> None:
|
|
267
293
|
"""
|
|
268
294
|
Invoked when device triggers the alarm.
|
|
269
295
|
"""
|
|
270
296
|
|
|
271
|
-
async def listen(self):
|
|
297
|
+
async def listen(self) -> None:
|
|
272
298
|
"""
|
|
273
299
|
Listens for notifications/alers from the device.
|
|
274
300
|
"""
|
|
@@ -288,15 +314,13 @@ class G90DeviceNotifications:
|
|
|
288
314
|
))
|
|
289
315
|
|
|
290
316
|
@property
|
|
291
|
-
def listener_started(self):
|
|
317
|
+
def listener_started(self) -> bool:
|
|
292
318
|
"""
|
|
293
319
|
Indicates if the listener of the device notifications has been started.
|
|
294
|
-
|
|
295
|
-
:rtype: bool
|
|
296
320
|
"""
|
|
297
321
|
return self._notification_transport is not None
|
|
298
322
|
|
|
299
|
-
def close(self):
|
|
323
|
+
def close(self) -> None:
|
|
300
324
|
"""
|
|
301
325
|
Closes the listener.
|
|
302
326
|
"""
|
pyg90alarm/discovery.py
CHANGED
|
@@ -21,8 +21,10 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Discovers G90 alarm panels.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
import asyncio
|
|
26
|
+
from typing import Any, List, Tuple
|
|
27
|
+
from dataclasses import dataclass
|
|
26
28
|
import logging
|
|
27
29
|
|
|
28
30
|
from .base_cmd import G90BaseCommand
|
|
@@ -32,45 +34,52 @@ from .const import G90Commands
|
|
|
32
34
|
_LOGGER = logging.getLogger(__name__)
|
|
33
35
|
|
|
34
36
|
|
|
37
|
+
@dataclass
|
|
38
|
+
class G90DiscoveredDevice(G90HostInfo):
|
|
39
|
+
"""
|
|
40
|
+
Represents discovered alarm panel.
|
|
41
|
+
"""
|
|
42
|
+
host: str
|
|
43
|
+
port: int
|
|
44
|
+
guid: str
|
|
45
|
+
|
|
46
|
+
|
|
35
47
|
class G90Discovery(G90BaseCommand):
|
|
36
48
|
"""
|
|
37
|
-
|
|
49
|
+
Discovers alarm panels.
|
|
38
50
|
"""
|
|
39
51
|
# pylint: disable=too-few-public-methods
|
|
40
|
-
def __init__(self, timeout=10, **kwargs):
|
|
41
|
-
"""
|
|
42
|
-
tbd
|
|
43
|
-
"""
|
|
52
|
+
def __init__(self, timeout: float = 10, **kwargs: Any):
|
|
44
53
|
# pylint: disable=too-many-arguments
|
|
45
54
|
super().__init__(code=G90Commands.GETHOSTINFO, timeout=timeout,
|
|
46
55
|
**kwargs)
|
|
47
|
-
self._discovered_devices = []
|
|
56
|
+
self._discovered_devices: List[G90DiscoveredDevice] = []
|
|
48
57
|
|
|
49
58
|
# Implementation of datagram protocol,
|
|
50
59
|
# https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols
|
|
51
|
-
def datagram_received(self, data, addr):
|
|
60
|
+
def datagram_received(self, data: bytes, addr: Tuple[str, int]) -> None:
|
|
52
61
|
"""
|
|
53
|
-
|
|
62
|
+
Invoked when some data is received.
|
|
54
63
|
"""
|
|
55
64
|
try:
|
|
56
65
|
ret = self.from_wire(data)
|
|
57
66
|
host_info = G90HostInfo(*ret)
|
|
58
67
|
_LOGGER.debug('Received from %s:%s: %s', addr[0], addr[1], ret)
|
|
59
|
-
res =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
res = G90DiscoveredDevice(
|
|
69
|
+
host=addr[0],
|
|
70
|
+
port=addr[1],
|
|
71
|
+
guid=host_info.host_guid,
|
|
72
|
+
**host_info._asdict()
|
|
73
|
+
)
|
|
65
74
|
_LOGGER.debug('Discovered device: %s', res)
|
|
66
75
|
self.add_device(res)
|
|
67
76
|
|
|
68
77
|
except Exception as exc: # pylint: disable=broad-except
|
|
69
78
|
_LOGGER.warning('Got exception, ignoring: %s', exc)
|
|
70
79
|
|
|
71
|
-
async def process(self):
|
|
80
|
+
async def process(self) -> G90Discovery:
|
|
72
81
|
"""
|
|
73
|
-
|
|
82
|
+
Initiates device discovery.
|
|
74
83
|
"""
|
|
75
84
|
_LOGGER.debug('Attempting device discovery...')
|
|
76
85
|
transport, _ = await self._create_connection()
|
|
@@ -78,17 +87,17 @@ class G90Discovery(G90BaseCommand):
|
|
|
78
87
|
await asyncio.sleep(self._timeout)
|
|
79
88
|
transport.close()
|
|
80
89
|
_LOGGER.debug('Discovered %s devices', len(self.devices))
|
|
81
|
-
return self
|
|
90
|
+
return self
|
|
82
91
|
|
|
83
92
|
@property
|
|
84
|
-
def devices(self):
|
|
93
|
+
def devices(self) -> List[G90DiscoveredDevice]:
|
|
85
94
|
"""
|
|
86
|
-
|
|
95
|
+
The list of discovered devices.
|
|
87
96
|
"""
|
|
88
97
|
return self._discovered_devices
|
|
89
98
|
|
|
90
|
-
def add_device(self, value):
|
|
99
|
+
def add_device(self, value: G90DiscoveredDevice) -> None:
|
|
91
100
|
"""
|
|
92
|
-
|
|
101
|
+
Adds discovered device to the list.
|
|
93
102
|
"""
|
|
94
103
|
self._discovered_devices.append(value)
|
pyg90alarm/entities/device.py
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"""
|
|
22
22
|
Provides interface to devices (switches) of G90 alarm panel.
|
|
23
23
|
"""
|
|
24
|
-
|
|
24
|
+
from __future__ import annotations
|
|
25
25
|
import logging
|
|
26
26
|
from .sensor import G90Sensor
|
|
27
27
|
from ..const import G90Commands
|
|
@@ -35,14 +35,14 @@ class G90Device(G90Sensor):
|
|
|
35
35
|
Interacts with device (relay) on G90 alarm panel.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
-
async def turn_on(self):
|
|
38
|
+
async def turn_on(self) -> None:
|
|
39
39
|
"""
|
|
40
40
|
Turns on the device (relay)
|
|
41
41
|
"""
|
|
42
42
|
await self.parent.command(G90Commands.CONTROLDEVICE,
|
|
43
43
|
[self.index, 0, self.subindex])
|
|
44
44
|
|
|
45
|
-
async def turn_off(self):
|
|
45
|
+
async def turn_off(self) -> None:
|
|
46
46
|
"""
|
|
47
47
|
Turns off the device (relay)
|
|
48
48
|
"""
|
|
@@ -50,12 +50,11 @@ class G90Device(G90Sensor):
|
|
|
50
50
|
[self.index, 1, self.subindex])
|
|
51
51
|
|
|
52
52
|
@property
|
|
53
|
-
def supports_enable_disable(self):
|
|
53
|
+
def supports_enable_disable(self) -> bool:
|
|
54
54
|
"""
|
|
55
55
|
Indicates if disabling/enabling the device (relay) is supported.
|
|
56
56
|
|
|
57
57
|
:return: Support for enabling/disabling the device
|
|
58
|
-
:rtype: bool
|
|
59
58
|
"""
|
|
60
59
|
# No support for manipulating of disable/enabled for the device, since
|
|
61
60
|
# single protocol entity read from the G90 alarm panel results in
|
|
@@ -65,11 +64,11 @@ class G90Device(G90Sensor):
|
|
|
65
64
|
# mostly.
|
|
66
65
|
return False
|
|
67
66
|
|
|
68
|
-
async def set_enabled(self, value):
|
|
67
|
+
async def set_enabled(self, value: bool) -> None:
|
|
69
68
|
"""
|
|
70
69
|
Changes the disabled/enabled state of the device (relay).
|
|
71
70
|
|
|
72
|
-
:param
|
|
71
|
+
:param value: Whether to enable or disable the device
|
|
73
72
|
"""
|
|
74
73
|
_LOGGER.warning(
|
|
75
74
|
'Manipulating with enable/disable for device is unsupported'
|