pyg90alarm 1.15.0__py3-none-any.whl → 1.15.2__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/alarm.py +25 -16
- pyg90alarm/const.py +2 -1
- pyg90alarm/device_notifications.py +55 -9
- {pyg90alarm-1.15.0.dist-info → pyg90alarm-1.15.2.dist-info}/METADATA +1 -1
- {pyg90alarm-1.15.0.dist-info → pyg90alarm-1.15.2.dist-info}/RECORD +8 -8
- {pyg90alarm-1.15.0.dist-info → pyg90alarm-1.15.2.dist-info}/WHEEL +1 -1
- {pyg90alarm-1.15.0.dist-info → pyg90alarm-1.15.2.dist-info}/LICENSE +0 -0
- {pyg90alarm-1.15.0.dist-info → pyg90alarm-1.15.2.dist-info}/top_level.txt +0 -0
pyg90alarm/alarm.py
CHANGED
|
@@ -60,7 +60,8 @@ from .const import (
|
|
|
60
60
|
G90Commands, REMOTE_PORT,
|
|
61
61
|
REMOTE_TARGETED_DISCOVERY_PORT,
|
|
62
62
|
LOCAL_TARGETED_DISCOVERY_PORT,
|
|
63
|
-
|
|
63
|
+
LOCAL_NOTIFICATIONS_HOST,
|
|
64
|
+
LOCAL_NOTIFICATIONS_PORT,
|
|
64
65
|
G90ArmDisarmTypes,
|
|
65
66
|
)
|
|
66
67
|
from .base_cmd import (G90BaseCommand, G90BaseCommandData)
|
|
@@ -100,22 +101,25 @@ if TYPE_CHECKING:
|
|
|
100
101
|
Callable[[int, str, bool], None],
|
|
101
102
|
Callable[[int, str, bool], Coroutine[None, None, None]]
|
|
102
103
|
]
|
|
103
|
-
SensorStateCallback = Union[
|
|
104
|
-
Callable[[bool], None],
|
|
105
|
-
Callable[[bool], Coroutine[None, None, None]]
|
|
106
|
-
]
|
|
107
104
|
LowBatteryCallback = Union[
|
|
108
105
|
Callable[[int, str], None],
|
|
109
106
|
Callable[[int, str], Coroutine[None, None, None]]
|
|
110
107
|
]
|
|
111
|
-
SensorLowBatteryCallback = Union[
|
|
112
|
-
Callable[[bool], None],
|
|
113
|
-
Callable[[bool], Coroutine[None, None, None]]
|
|
114
|
-
]
|
|
115
108
|
ArmDisarmCallback = Union[
|
|
116
109
|
Callable[[G90ArmDisarmTypes], None],
|
|
117
110
|
Callable[[G90ArmDisarmTypes], Coroutine[None, None, None]]
|
|
118
111
|
]
|
|
112
|
+
# Sensor-related callbacks for `G90Sensor` class - despite that class
|
|
113
|
+
# stores them, the invication is done by the `G90Alarm` class hence these
|
|
114
|
+
# are defined here
|
|
115
|
+
SensorStateCallback = Union[
|
|
116
|
+
Callable[[bool], None],
|
|
117
|
+
Callable[[bool], Coroutine[None, None, None]]
|
|
118
|
+
]
|
|
119
|
+
SensorLowBatteryCallback = Union[
|
|
120
|
+
Callable[[], None],
|
|
121
|
+
Callable[[], Coroutine[None, None, None]]
|
|
122
|
+
]
|
|
119
123
|
|
|
120
124
|
|
|
121
125
|
# pylint: disable=too-many-public-methods
|
|
@@ -137,11 +141,14 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
137
141
|
# pylint: disable=too-many-instance-attributes,too-many-arguments
|
|
138
142
|
def __init__(self, host: str, port: int = REMOTE_PORT,
|
|
139
143
|
reset_occupancy_interval: float = 3.0,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
super().__init__(
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
notifications_local_host: str = LOCAL_NOTIFICATIONS_HOST,
|
|
145
|
+
notifications_local_port: int = LOCAL_NOTIFICATIONS_PORT):
|
|
146
|
+
super().__init__(
|
|
147
|
+
local_host=notifications_local_host,
|
|
148
|
+
local_port=notifications_local_port
|
|
149
|
+
)
|
|
150
|
+
self._host: str = host
|
|
151
|
+
self._port: int = port
|
|
145
152
|
self._sensors: List[G90Sensor] = []
|
|
146
153
|
self._devices: List[G90Device] = []
|
|
147
154
|
self._notifications: Optional[G90DeviceNotifications] = None
|
|
@@ -333,7 +340,9 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
333
340
|
:return: Device information
|
|
334
341
|
"""
|
|
335
342
|
res = await self.command(G90Commands.GETHOSTINFO)
|
|
336
|
-
|
|
343
|
+
info = G90HostInfo(*res)
|
|
344
|
+
self.device_id = info.host_guid
|
|
345
|
+
return info
|
|
337
346
|
|
|
338
347
|
@property
|
|
339
348
|
async def host_status(self) -> G90HostStatus:
|
|
@@ -810,7 +819,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
810
819
|
# code as alert, as if it came from the device and its
|
|
811
820
|
# notifications port
|
|
812
821
|
self._handle_alert(
|
|
813
|
-
(self._host, self.
|
|
822
|
+
(self._host, self._notifications_local_port),
|
|
814
823
|
item.as_device_alert()
|
|
815
824
|
)
|
|
816
825
|
|
pyg90alarm/const.py
CHANGED
|
@@ -28,7 +28,8 @@ from typing import Optional
|
|
|
28
28
|
REMOTE_PORT = 12368
|
|
29
29
|
REMOTE_TARGETED_DISCOVERY_PORT = 12900
|
|
30
30
|
LOCAL_TARGETED_DISCOVERY_PORT = 12901
|
|
31
|
-
|
|
31
|
+
LOCAL_NOTIFICATIONS_HOST = '0.0.0.0'
|
|
32
|
+
LOCAL_NOTIFICATIONS_PORT = 12901
|
|
32
33
|
|
|
33
34
|
CMD_PAGE_SIZE = 10
|
|
34
35
|
|
|
@@ -41,7 +41,6 @@ from .const import (
|
|
|
41
41
|
G90AlertStates,
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
|
|
45
44
|
_LOGGER = logging.getLogger(__name__)
|
|
46
45
|
|
|
47
46
|
|
|
@@ -107,12 +106,29 @@ class G90DeviceAlert: # pylint: disable=too-many-instance-attributes
|
|
|
107
106
|
class G90DeviceNotifications(DatagramProtocol):
|
|
108
107
|
"""
|
|
109
108
|
Implements support for notifications/alerts sent by alarm panel.
|
|
109
|
+
|
|
110
|
+
There is a basic check to ensure only notifications/alerts from the correct
|
|
111
|
+
device are processed - the check uses the host and port of the device, and
|
|
112
|
+
the device ID (GUID) that is set by the ancestor class that implements the
|
|
113
|
+
commands (e.g. :class:`G90Alarm`). The latter to work correctly needs a
|
|
114
|
+
command to be performed first, one that fetches device GUID and then stores
|
|
115
|
+
it using :attr:`.device_id` (e.g. :meth:`G90Alarm.get_host_info`).
|
|
110
116
|
"""
|
|
111
|
-
def __init__(self,
|
|
117
|
+
def __init__(self, local_port: int, local_host: str):
|
|
112
118
|
# pylint: disable=too-many-arguments
|
|
113
119
|
self._notification_transport: Optional[BaseTransport] = None
|
|
114
|
-
self.
|
|
115
|
-
self.
|
|
120
|
+
self._notifications_local_host = local_host
|
|
121
|
+
self._notifications_local_port = local_port
|
|
122
|
+
# Host/port of the device is configured to communicating via commands.
|
|
123
|
+
# Inteded to validate if notifications/alert are received from the
|
|
124
|
+
# correct device.
|
|
125
|
+
self._host: Optional[str] = None
|
|
126
|
+
self._port: Optional[int] = None
|
|
127
|
+
# Same but for device ID (GUID) - the notifications logic uses it to
|
|
128
|
+
# perform validation, but doesn't set it from messages received (it
|
|
129
|
+
# will diminish the purpose of the validation, should be done by an
|
|
130
|
+
# ancestor class).
|
|
131
|
+
self._device_id: Optional[str] = None
|
|
116
132
|
|
|
117
133
|
def _handle_notification(
|
|
118
134
|
self, addr: Tuple[str, int], notification: G90Notification
|
|
@@ -145,6 +161,15 @@ class G90DeviceNotifications(DatagramProtocol):
|
|
|
145
161
|
def _handle_alert(
|
|
146
162
|
self, addr: Tuple[str, int], alert: G90DeviceAlert
|
|
147
163
|
) -> None:
|
|
164
|
+
# Stop processing when alert is received from the device with different
|
|
165
|
+
# GUID
|
|
166
|
+
if self.device_id and alert.device_id != self.device_id:
|
|
167
|
+
_LOGGER.error(
|
|
168
|
+
"Received alert from wrong device: expected '%s', got '%s'",
|
|
169
|
+
self.device_id, alert.device_id
|
|
170
|
+
)
|
|
171
|
+
return
|
|
172
|
+
|
|
148
173
|
if alert.type == G90AlertTypes.DOOR_OPEN_CLOSE:
|
|
149
174
|
if alert.state in (
|
|
150
175
|
G90AlertStates.DOOR_OPEN, G90AlertStates.DOOR_CLOSE
|
|
@@ -219,10 +244,17 @@ class G90DeviceNotifications(DatagramProtocol):
|
|
|
219
244
|
def datagram_received( # pylint:disable=R0911
|
|
220
245
|
self, data: bytes, addr: Tuple[str, int]
|
|
221
246
|
) -> None:
|
|
222
|
-
|
|
223
247
|
"""
|
|
224
|
-
Invoked
|
|
248
|
+
Invoked when datagram is received from the device.
|
|
225
249
|
"""
|
|
250
|
+
if self._host and self._host != addr[0]:
|
|
251
|
+
_LOGGER.error(
|
|
252
|
+
"Received notification/alert from wrong host '%s',"
|
|
253
|
+
" expected from '%s'",
|
|
254
|
+
addr[0], self._host
|
|
255
|
+
)
|
|
256
|
+
return
|
|
257
|
+
|
|
226
258
|
s_data = data.decode('utf-8')
|
|
227
259
|
if not s_data.endswith('\0'):
|
|
228
260
|
_LOGGER.error('Missing end marker in data')
|
|
@@ -304,13 +336,13 @@ class G90DeviceNotifications(DatagramProtocol):
|
|
|
304
336
|
loop = asyncio.get_event_loop()
|
|
305
337
|
|
|
306
338
|
_LOGGER.debug('Creating UDP endpoint for %s:%s',
|
|
307
|
-
self.
|
|
308
|
-
self.
|
|
339
|
+
self._notifications_local_host,
|
|
340
|
+
self._notifications_local_port)
|
|
309
341
|
(self._notification_transport,
|
|
310
342
|
_protocol) = await loop.create_datagram_endpoint(
|
|
311
343
|
lambda: self,
|
|
312
344
|
local_addr=(
|
|
313
|
-
self.
|
|
345
|
+
self._notifications_local_host, self._notifications_local_port
|
|
314
346
|
))
|
|
315
347
|
|
|
316
348
|
@property
|
|
@@ -328,3 +360,17 @@ class G90DeviceNotifications(DatagramProtocol):
|
|
|
328
360
|
_LOGGER.debug('No longer listening for device notifications')
|
|
329
361
|
self._notification_transport.close()
|
|
330
362
|
self._notification_transport = None
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def device_id(self) -> Optional[str]:
|
|
366
|
+
"""
|
|
367
|
+
The ID (GUID) of the panel being communicated with thru commands.
|
|
368
|
+
|
|
369
|
+
Available when any panel command receives it from the device
|
|
370
|
+
(:meth:`G90Alarm.get_host_info` currently).
|
|
371
|
+
"""
|
|
372
|
+
return self._device_id
|
|
373
|
+
|
|
374
|
+
@device_id.setter
|
|
375
|
+
def device_id(self, device_id: str) -> None:
|
|
376
|
+
self._device_id = device_id
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
pyg90alarm/__init__.py,sha256=5AITRm5jZSzuQaL7PS8fZZMZb4-IuGRhSqyAdfTt0Cs,2236
|
|
2
|
-
pyg90alarm/alarm.py,sha256=
|
|
2
|
+
pyg90alarm/alarm.py,sha256=VssyGxYXb-r-fb8TdN2NfLaIPr1Zq8ogdGUV521bjsQ,32414
|
|
3
3
|
pyg90alarm/base_cmd.py,sha256=wu2v_RKpcPHxUW4HBDLWcUFMzPsGooY4o2Rc0LgcanU,9754
|
|
4
4
|
pyg90alarm/callback.py,sha256=3JsD_JChmZD24OyjaCP-PxxuBDBX7myGYhkM4RN7bk4,3742
|
|
5
5
|
pyg90alarm/config.py,sha256=2YtIgdT7clQXmYvkdn_fhIdS05CY8E1Yc90R8_tAmRI,1961
|
|
6
|
-
pyg90alarm/const.py,sha256=
|
|
7
|
-
pyg90alarm/device_notifications.py,sha256=
|
|
6
|
+
pyg90alarm/const.py,sha256=1nzx2rueRdAdv1J5nsfS-c_9a4tcM4_z5sWwCNG7tiQ,6097
|
|
7
|
+
pyg90alarm/device_notifications.py,sha256=1nOeG-YrtaoD5wsJUfUM1kV7JIXv3KwnN2QuJ-C--kQ,13238
|
|
8
8
|
pyg90alarm/discovery.py,sha256=fwyBHDCKGej06OwhpbVCHYTRU9WWkeYysAFgv3FiwqI,3575
|
|
9
9
|
pyg90alarm/exceptions.py,sha256=eiOcRe7D18EIPyPFDNU9DdFgbnkwPmkiLl8lGPOhBNw,1475
|
|
10
10
|
pyg90alarm/history.py,sha256=GnCVRsQNV2x9g6KngBBvvIUnTAIyXKX2nVKF_aFA0oM,7160
|
|
@@ -20,8 +20,8 @@ pyg90alarm/definitions/sensors.py,sha256=rKOu21ZpI44xk6aMh_vBjniFqnsNTc1CKwAvnv4
|
|
|
20
20
|
pyg90alarm/entities/__init__.py,sha256=hHb6AOiC4Tz--rOWiiICMdLaZDs1Tf_xpWk_HeS_gO4,66
|
|
21
21
|
pyg90alarm/entities/device.py,sha256=f_LHvKCAqTEebZ4mrRh3CpPUI7o-OvpvOfyTRCbftJs,2818
|
|
22
22
|
pyg90alarm/entities/sensor.py,sha256=4r8ouAYTZB8ih8I4ncWdQOaifYsRxaC-ukY9jvnrRvk,16139
|
|
23
|
-
pyg90alarm-1.15.
|
|
24
|
-
pyg90alarm-1.15.
|
|
25
|
-
pyg90alarm-1.15.
|
|
26
|
-
pyg90alarm-1.15.
|
|
27
|
-
pyg90alarm-1.15.
|
|
23
|
+
pyg90alarm-1.15.2.dist-info/LICENSE,sha256=f884inRbeNv-O-hbwz62Ro_1J8xiHRTnJ2cCx6A0WvU,1070
|
|
24
|
+
pyg90alarm-1.15.2.dist-info/METADATA,sha256=8aflU3AAg8Jb3a0wsqqyumSJPnO_HokLb3ZM3z3coNk,7663
|
|
25
|
+
pyg90alarm-1.15.2.dist-info/WHEEL,sha256=ixB2d4u7mugx_bCBycvM9OzZ5yD7NmPXFRtKlORZS2Y,91
|
|
26
|
+
pyg90alarm-1.15.2.dist-info/top_level.txt,sha256=czHiGxYMyTk5QEDTDb0EpPiKqUMRa8zI4zx58Ii409M,11
|
|
27
|
+
pyg90alarm-1.15.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|