pyg90alarm 1.15.1__tar.gz → 1.15.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.
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/PKG-INFO +1 -1
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/alarm.py +14 -8
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/const.py +2 -1
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/device_notifications.py +55 -9
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm.egg-info/PKG-INFO +1 -1
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/test_alarm.py +14 -14
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/test_notifications.py +104 -16
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/.github/CODEOWNERS +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/.github/workflows/main.yml +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/.gitignore +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/.pylintrc +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/.readthedocs.yaml +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/LICENSE +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/MANIFEST.in +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/README.rst +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/.DS_Store +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/.gitignore +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/api-docs.rst +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/conf.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/index.rst +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/protocol.rst +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/docs/requirements.txt +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/pyproject.toml +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/setup.cfg +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/setup.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/sonar-project.properties +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/__init__.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/base_cmd.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/callback.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/config.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/definitions/__init__.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/definitions/sensors.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/discovery.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/entities/__init__.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/entities/device.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/entities/sensor.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/exceptions.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/history.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/host_info.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/host_status.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/paginated_cmd.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/paginated_result.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/py.typed +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/targeted_discovery.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm/user_data_crc.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm.egg-info/SOURCES.txt +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm.egg-info/dependency_links.txt +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm.egg-info/requires.txt +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/src/pyg90alarm.egg-info/top_level.txt +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/__init__.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/conftest.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/device_mock.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/test_base_commands.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/test_discovery.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tests/test_paginated_commands.py +0 -0
- {pyg90alarm-1.15.1 → pyg90alarm-1.15.2}/tox.ini +0 -0
|
@@ -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)
|
|
@@ -140,11 +141,14 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
140
141
|
# pylint: disable=too-many-instance-attributes,too-many-arguments
|
|
141
142
|
def __init__(self, host: str, port: int = REMOTE_PORT,
|
|
142
143
|
reset_occupancy_interval: float = 3.0,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
super().__init__(
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
148
152
|
self._sensors: List[G90Sensor] = []
|
|
149
153
|
self._devices: List[G90Device] = []
|
|
150
154
|
self._notifications: Optional[G90DeviceNotifications] = None
|
|
@@ -336,7 +340,9 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
336
340
|
:return: Device information
|
|
337
341
|
"""
|
|
338
342
|
res = await self.command(G90Commands.GETHOSTINFO)
|
|
339
|
-
|
|
343
|
+
info = G90HostInfo(*res)
|
|
344
|
+
self.device_id = info.host_guid
|
|
345
|
+
return info
|
|
340
346
|
|
|
341
347
|
@property
|
|
342
348
|
async def host_status(self) -> G90HostStatus:
|
|
@@ -813,7 +819,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
813
819
|
# code as alert, as if it came from the device and its
|
|
814
820
|
# notifications port
|
|
815
821
|
self._handle_alert(
|
|
816
|
-
(self._host, self.
|
|
822
|
+
(self._host, self._notifications_local_port),
|
|
817
823
|
item.as_device_alert()
|
|
818
824
|
)
|
|
819
825
|
|
|
@@ -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
|
|
@@ -308,8 +308,8 @@ async def test_sensor_event(mock_device: DeviceMock) -> None:
|
|
|
308
308
|
reset_interval = 0.5
|
|
309
309
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
310
310
|
reset_occupancy_interval=reset_interval,
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
notifications_local_host=mock_device.notification_host,
|
|
312
|
+
notifications_local_port=mock_device.notification_port)
|
|
313
313
|
|
|
314
314
|
sensors = await g90.get_sensors()
|
|
315
315
|
prop_sensors = await g90.sensors
|
|
@@ -352,8 +352,8 @@ async def test_sensor_low_battery_event(mock_device: DeviceMock) -> None:
|
|
|
352
352
|
Tests for sensor low battery callback.
|
|
353
353
|
"""
|
|
354
354
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
notifications_local_host=mock_device.notification_host,
|
|
356
|
+
notifications_local_port=mock_device.notification_port)
|
|
357
357
|
|
|
358
358
|
sensors = await g90.get_sensors()
|
|
359
359
|
prop_sensors = await g90.sensors
|
|
@@ -401,8 +401,8 @@ async def test_armdisarm_callback(mock_device: DeviceMock) -> None:
|
|
|
401
401
|
armdisarm_cb = MagicMock()
|
|
402
402
|
armdisarm_cb.side_effect = lambda *args: future.set_result(True)
|
|
403
403
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
404
|
-
|
|
405
|
-
|
|
404
|
+
notifications_local_host=mock_device.notification_host,
|
|
405
|
+
notifications_local_port=mock_device.notification_port)
|
|
406
406
|
g90.armdisarm_callback = armdisarm_cb
|
|
407
407
|
await g90.listen_device_notifications()
|
|
408
408
|
await mock_device.send_next_notification()
|
|
@@ -434,8 +434,8 @@ async def test_door_open_close_callback(mock_device: DeviceMock) -> None:
|
|
|
434
434
|
door_open_close_cb.side_effect = lambda *args: future.set_result(True)
|
|
435
435
|
|
|
436
436
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
437
|
-
|
|
438
|
-
|
|
437
|
+
notifications_local_host=mock_device.notification_host,
|
|
438
|
+
notifications_local_port=mock_device.notification_port)
|
|
439
439
|
g90.door_open_close_callback = door_open_close_cb
|
|
440
440
|
|
|
441
441
|
# Simulate two device alerts - for opening (this one) and then closing the
|
|
@@ -492,8 +492,8 @@ async def test_alarm_callback(mock_device: DeviceMock) -> None:
|
|
|
492
492
|
alarm_cb.side_effect = lambda *args: future.set_result(True)
|
|
493
493
|
|
|
494
494
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
495
|
-
|
|
496
|
-
|
|
495
|
+
notifications_local_host=mock_device.notification_host,
|
|
496
|
+
notifications_local_port=mock_device.notification_port)
|
|
497
497
|
sensors = await g90.get_sensors()
|
|
498
498
|
# Set extra data for the 1st sensor
|
|
499
499
|
sensors[0].extra_data = 'Dummy extra data'
|
|
@@ -753,8 +753,8 @@ async def test_sms_alert_when_armed(mock_device: DeviceMock) -> None:
|
|
|
753
753
|
armdisarm_cb = MagicMock()
|
|
754
754
|
armdisarm_cb.side_effect = lambda *args: future.set_result(True)
|
|
755
755
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
756
|
-
|
|
757
|
-
|
|
756
|
+
notifications_local_host=mock_device.notification_host,
|
|
757
|
+
notifications_local_port=mock_device.notification_port)
|
|
758
758
|
g90.armdisarm_callback = armdisarm_cb
|
|
759
759
|
g90.sms_alert_when_armed = True
|
|
760
760
|
await g90.listen_device_notifications()
|
|
@@ -787,8 +787,8 @@ async def test_sms_alert_when_disarmed(mock_device: DeviceMock) -> None:
|
|
|
787
787
|
armdisarm_cb = MagicMock()
|
|
788
788
|
armdisarm_cb.side_effect = lambda *args: future.set_result(True)
|
|
789
789
|
g90 = G90Alarm(host=mock_device.host, port=mock_device.port,
|
|
790
|
-
|
|
791
|
-
|
|
790
|
+
notifications_local_host=mock_device.notification_host,
|
|
791
|
+
notifications_local_port=mock_device.notification_port)
|
|
792
792
|
g90.armdisarm_callback = armdisarm_cb
|
|
793
793
|
g90.sms_alert_when_armed = True
|
|
794
794
|
await g90.listen_device_notifications()
|
|
@@ -10,6 +10,7 @@ from pytest import LogCaptureFixture
|
|
|
10
10
|
from pyg90alarm.device_notifications import (
|
|
11
11
|
G90DeviceNotifications,
|
|
12
12
|
)
|
|
13
|
+
from pyg90alarm.alarm import G90Alarm
|
|
13
14
|
|
|
14
15
|
from .device_mock import DeviceMock
|
|
15
16
|
|
|
@@ -24,7 +25,8 @@ async def test_device_notification_missing_header(
|
|
|
24
25
|
Verifies that missing header in device notification is handled correctly.
|
|
25
26
|
"""
|
|
26
27
|
notifications = G90DeviceNotifications(
|
|
27
|
-
|
|
28
|
+
local_host=mock_device.notification_host,
|
|
29
|
+
local_port=mock_device.notification_port
|
|
28
30
|
)
|
|
29
31
|
caplog.set_level('ERROR')
|
|
30
32
|
await notifications.listen()
|
|
@@ -49,7 +51,8 @@ async def test_device_notification_malformed_message(
|
|
|
49
51
|
correctly.
|
|
50
52
|
"""
|
|
51
53
|
notifications = G90DeviceNotifications(
|
|
52
|
-
|
|
54
|
+
local_host=mock_device.notification_host,
|
|
55
|
+
local_port=mock_device.notification_port
|
|
53
56
|
)
|
|
54
57
|
caplog.set_level('ERROR')
|
|
55
58
|
await notifications.listen()
|
|
@@ -72,7 +75,8 @@ async def test_device_notification_missing_end_marker(
|
|
|
72
75
|
correctly.
|
|
73
76
|
"""
|
|
74
77
|
notifications = G90DeviceNotifications(
|
|
75
|
-
|
|
78
|
+
local_host=mock_device.notification_host,
|
|
79
|
+
local_port=mock_device.notification_port
|
|
76
80
|
)
|
|
77
81
|
caplog.set_level('ERROR')
|
|
78
82
|
await notifications.listen()
|
|
@@ -91,7 +95,8 @@ async def test_wrong_device_notification_format(
|
|
|
91
95
|
Verifies that wrong device notification format is handled correctly.
|
|
92
96
|
"""
|
|
93
97
|
notifications = G90DeviceNotifications(
|
|
94
|
-
|
|
98
|
+
local_host=mock_device.notification_host,
|
|
99
|
+
local_port=mock_device.notification_port
|
|
95
100
|
)
|
|
96
101
|
caplog.set_level('ERROR')
|
|
97
102
|
await notifications.listen()
|
|
@@ -114,7 +119,8 @@ async def test_wrong_device_alert_format(
|
|
|
114
119
|
Verifies that wrong device alert format is handled correctly.
|
|
115
120
|
"""
|
|
116
121
|
notifications = G90DeviceNotifications(
|
|
117
|
-
|
|
122
|
+
local_host=mock_device.notification_host,
|
|
123
|
+
local_port=mock_device.notification_port
|
|
118
124
|
)
|
|
119
125
|
|
|
120
126
|
caplog.set_level('ERROR')
|
|
@@ -140,7 +146,8 @@ async def test_unknown_device_notification(
|
|
|
140
146
|
Verifies that unknown device notification is handled correctly.
|
|
141
147
|
"""
|
|
142
148
|
notifications = G90DeviceNotifications(
|
|
143
|
-
|
|
149
|
+
local_host=mock_device.notification_host,
|
|
150
|
+
local_port=mock_device.notification_port
|
|
144
151
|
)
|
|
145
152
|
caplog.set_level('WARNING')
|
|
146
153
|
await notifications.listen()
|
|
@@ -164,7 +171,8 @@ async def test_unknown_device_alert(
|
|
|
164
171
|
Verifies that unknown device alert is handled correctly.
|
|
165
172
|
"""
|
|
166
173
|
notifications = G90DeviceNotifications(
|
|
167
|
-
|
|
174
|
+
local_host=mock_device.notification_host,
|
|
175
|
+
local_port=mock_device.notification_port
|
|
168
176
|
)
|
|
169
177
|
caplog.set_level('WARNING')
|
|
170
178
|
await notifications.listen()
|
|
@@ -179,6 +187,79 @@ async def test_unknown_device_alert(
|
|
|
179
187
|
notifications.close()
|
|
180
188
|
|
|
181
189
|
|
|
190
|
+
@pytest.mark.g90device(notification_data=[
|
|
191
|
+
b'[208,[999,100,1,1,"Hall","DUMMYGUID",'
|
|
192
|
+
b'1631545189,0,[""]]]\0',
|
|
193
|
+
])
|
|
194
|
+
async def test_wrong_host(
|
|
195
|
+
mock_device: DeviceMock, caplog: LogCaptureFixture
|
|
196
|
+
) -> None:
|
|
197
|
+
"""
|
|
198
|
+
Verifies that unknown device alert is handled correctly.
|
|
199
|
+
"""
|
|
200
|
+
g90 = G90Alarm(
|
|
201
|
+
host='1.2.3.4',
|
|
202
|
+
notifications_local_host=mock_device.notification_host,
|
|
203
|
+
notifications_local_port=mock_device.notification_port
|
|
204
|
+
)
|
|
205
|
+
# pylint: disable=protected-access
|
|
206
|
+
g90._handle_alert = ( # type: ignore[method-assign]
|
|
207
|
+
MagicMock()
|
|
208
|
+
)
|
|
209
|
+
# pylint: disable=protected-access
|
|
210
|
+
g90._handle_notification = ( # type: ignore[method-assign]
|
|
211
|
+
MagicMock()
|
|
212
|
+
)
|
|
213
|
+
caplog.set_level('WARNING')
|
|
214
|
+
await g90.listen()
|
|
215
|
+
await mock_device.send_next_notification()
|
|
216
|
+
assert ''.join(caplog.messages) == (
|
|
217
|
+
"Received notification/alert from wrong host '127.0.0.1'"
|
|
218
|
+
", expected from '1.2.3.4'"
|
|
219
|
+
)
|
|
220
|
+
g90.close()
|
|
221
|
+
# pylint: disable=protected-access
|
|
222
|
+
g90._handle_alert.assert_not_called()
|
|
223
|
+
# pylint: disable=protected-access
|
|
224
|
+
g90._handle_notification.assert_not_called()
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@pytest.mark.g90device(
|
|
228
|
+
sent_data=[
|
|
229
|
+
b'ISTART[206,'
|
|
230
|
+
b'["DUMMYGUID","DUMMYPRODUCT",'
|
|
231
|
+
b'"1.2","1.1","206","206",3,3,0,2,"4242",50,100]]IEND\0',
|
|
232
|
+
],
|
|
233
|
+
notification_data=[
|
|
234
|
+
b'[208,[2,4,0,0,"","DIFFERENTGUID",1630876128,0,[""]]]\0'
|
|
235
|
+
],
|
|
236
|
+
)
|
|
237
|
+
async def test_wrong_device_guid(
|
|
238
|
+
mock_device: DeviceMock, caplog: LogCaptureFixture
|
|
239
|
+
) -> None:
|
|
240
|
+
"""
|
|
241
|
+
Verifies that alert from device with different GUID is ignored.
|
|
242
|
+
"""
|
|
243
|
+
g90 = G90Alarm(
|
|
244
|
+
host=mock_device.host, port=mock_device.port,
|
|
245
|
+
notifications_local_host=mock_device.notification_host,
|
|
246
|
+
notifications_local_port=mock_device.notification_port
|
|
247
|
+
)
|
|
248
|
+
caplog.set_level('WARNING')
|
|
249
|
+
# The command will fetch the host info and store the GIUD
|
|
250
|
+
await g90.get_host_info()
|
|
251
|
+
g90.on_armdisarm = MagicMock() # type: ignore[method-assign]
|
|
252
|
+
await g90.listen()
|
|
253
|
+
await mock_device.send_next_notification()
|
|
254
|
+
assert ''.join(caplog.messages) == (
|
|
255
|
+
"Received alert from wrong device: expected 'DUMMYGUID'"
|
|
256
|
+
", got 'DIFFERENTGUID'"
|
|
257
|
+
)
|
|
258
|
+
g90.close()
|
|
259
|
+
# Verify the associated callback was not called
|
|
260
|
+
g90.on_armdisarm.assert_not_called()
|
|
261
|
+
|
|
262
|
+
|
|
182
263
|
@pytest.mark.g90device(notification_data=[
|
|
183
264
|
b'[170,[5,[100,"Hall"]]]\0',
|
|
184
265
|
])
|
|
@@ -188,8 +269,8 @@ async def test_sensor_callback(mock_device: DeviceMock) -> None:
|
|
|
188
269
|
"""
|
|
189
270
|
future = asyncio.get_running_loop().create_future()
|
|
190
271
|
notifications = G90DeviceNotifications(
|
|
191
|
-
|
|
192
|
-
|
|
272
|
+
local_host=mock_device.notification_host,
|
|
273
|
+
local_port=mock_device.notification_port,
|
|
193
274
|
)
|
|
194
275
|
|
|
195
276
|
notifications.on_sensor_activity = ( # type: ignore[method-assign]
|
|
@@ -216,7 +297,8 @@ async def test_armdisarm_notification_callback(
|
|
|
216
297
|
"""
|
|
217
298
|
future = asyncio.get_running_loop().create_future()
|
|
218
299
|
notifications = G90DeviceNotifications(
|
|
219
|
-
|
|
300
|
+
local_host=mock_device.notification_host,
|
|
301
|
+
local_port=mock_device.notification_port
|
|
220
302
|
)
|
|
221
303
|
notifications.on_armdisarm = MagicMock() # type: ignore[method-assign]
|
|
222
304
|
notifications.on_armdisarm.side_effect = (
|
|
@@ -238,7 +320,8 @@ async def test_armdisarm_alert_callback(mock_device: DeviceMock) -> None:
|
|
|
238
320
|
"""
|
|
239
321
|
future = asyncio.get_running_loop().create_future()
|
|
240
322
|
notifications = G90DeviceNotifications(
|
|
241
|
-
|
|
323
|
+
local_host=mock_device.notification_host,
|
|
324
|
+
local_port=mock_device.notification_port
|
|
242
325
|
)
|
|
243
326
|
notifications.on_armdisarm = MagicMock() # type: ignore[method-assign]
|
|
244
327
|
notifications.on_armdisarm.side_effect = (
|
|
@@ -260,7 +343,8 @@ async def test_door_open_callback(mock_device: DeviceMock) -> None:
|
|
|
260
343
|
"""
|
|
261
344
|
future = asyncio.get_running_loop().create_future()
|
|
262
345
|
notifications = G90DeviceNotifications(
|
|
263
|
-
|
|
346
|
+
local_host=mock_device.notification_host,
|
|
347
|
+
local_port=mock_device.notification_port
|
|
264
348
|
)
|
|
265
349
|
|
|
266
350
|
notifications.on_door_open_close = ( # type: ignore[method-assign]
|
|
@@ -285,7 +369,8 @@ async def test_door_close_callback(mock_device: DeviceMock) -> None:
|
|
|
285
369
|
"""
|
|
286
370
|
future = asyncio.get_running_loop().create_future()
|
|
287
371
|
notifications = G90DeviceNotifications(
|
|
288
|
-
|
|
372
|
+
local_host=mock_device.notification_host,
|
|
373
|
+
local_port=mock_device.notification_port
|
|
289
374
|
)
|
|
290
375
|
|
|
291
376
|
notifications.on_door_open_close = ( # type: ignore[method-assign]
|
|
@@ -312,7 +397,8 @@ async def test_doorbell_callback(mock_device: DeviceMock) -> None:
|
|
|
312
397
|
"""
|
|
313
398
|
future = asyncio.get_running_loop().create_future()
|
|
314
399
|
notifications = G90DeviceNotifications(
|
|
315
|
-
|
|
400
|
+
local_host=mock_device.notification_host,
|
|
401
|
+
local_port=mock_device.notification_port
|
|
316
402
|
)
|
|
317
403
|
|
|
318
404
|
notifications.on_door_open_close = ( # type: ignore[method-assign]
|
|
@@ -339,7 +425,8 @@ async def test_alarm_callback(mock_device: DeviceMock) -> None:
|
|
|
339
425
|
"""
|
|
340
426
|
future = asyncio.get_running_loop().create_future()
|
|
341
427
|
notifications = G90DeviceNotifications(
|
|
342
|
-
|
|
428
|
+
local_host=mock_device.notification_host,
|
|
429
|
+
local_port=mock_device.notification_port
|
|
343
430
|
)
|
|
344
431
|
notifications.on_alarm = MagicMock() # type: ignore[method-assign]
|
|
345
432
|
notifications.on_alarm.side_effect = (
|
|
@@ -361,7 +448,8 @@ async def test_low_battery_callback(mock_device: DeviceMock) -> None:
|
|
|
361
448
|
"""
|
|
362
449
|
future = asyncio.get_running_loop().create_future()
|
|
363
450
|
notifications = G90DeviceNotifications(
|
|
364
|
-
|
|
451
|
+
local_host=mock_device.notification_host,
|
|
452
|
+
local_port=mock_device.notification_port
|
|
365
453
|
)
|
|
366
454
|
notifications.on_low_battery = MagicMock() # type: ignore[method-assign]
|
|
367
455
|
notifications.on_low_battery.side_effect = (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|