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
pyg90alarm/alarm.py
CHANGED
|
@@ -48,8 +48,17 @@ G90HostInfo(host_guid='<...>',
|
|
|
48
48
|
wifi_signal_level=100)
|
|
49
49
|
|
|
50
50
|
"""
|
|
51
|
+
from __future__ import annotations
|
|
51
52
|
import asyncio
|
|
53
|
+
from asyncio import Task
|
|
52
54
|
import logging
|
|
55
|
+
from typing import (
|
|
56
|
+
TYPE_CHECKING, Any, List, Optional, AsyncGenerator,
|
|
57
|
+
Callable, Coroutine, Union
|
|
58
|
+
)
|
|
59
|
+
from typing_extensions import (
|
|
60
|
+
TypeAlias
|
|
61
|
+
)
|
|
53
62
|
from .const import (
|
|
54
63
|
G90Commands, REMOTE_PORT,
|
|
55
64
|
REMOTE_TARGETED_DISCOVERY_PORT,
|
|
@@ -57,15 +66,17 @@ from .const import (
|
|
|
57
66
|
NOTIFICATIONS_PORT,
|
|
58
67
|
G90ArmDisarmTypes,
|
|
59
68
|
)
|
|
60
|
-
from .base_cmd import G90BaseCommand
|
|
61
|
-
from .paginated_result import G90PaginatedResult
|
|
69
|
+
from .base_cmd import (G90BaseCommand, G90BaseCommandData)
|
|
70
|
+
from .paginated_result import G90PaginatedResult, G90PaginatedResponse
|
|
62
71
|
from .entities.sensor import (G90Sensor, G90SensorTypes)
|
|
63
72
|
from .entities.device import G90Device
|
|
64
73
|
from .device_notifications import (
|
|
65
74
|
G90DeviceNotifications,
|
|
66
75
|
)
|
|
67
|
-
from .discovery import G90Discovery
|
|
68
|
-
from .targeted_discovery import
|
|
76
|
+
from .discovery import G90Discovery, G90DiscoveredDevice
|
|
77
|
+
from .targeted_discovery import (
|
|
78
|
+
G90TargetedDiscovery, G90DiscoveredDeviceTargeted,
|
|
79
|
+
)
|
|
69
80
|
from .host_info import G90HostInfo
|
|
70
81
|
from .host_status import G90HostStatus
|
|
71
82
|
from .config import (G90AlertConfig, G90AlertConfigFlags)
|
|
@@ -76,6 +87,39 @@ from .exceptions import G90Error, G90TimeoutError
|
|
|
76
87
|
|
|
77
88
|
_LOGGER = logging.getLogger(__name__)
|
|
78
89
|
|
|
90
|
+
if TYPE_CHECKING:
|
|
91
|
+
# Type alias for the callback functions available to the user, should be
|
|
92
|
+
# compatible with `G90Callback.Callback` type, since `G90Callback.invoke`
|
|
93
|
+
# is used to invoke them
|
|
94
|
+
AlarmCallback: TypeAlias = Union[
|
|
95
|
+
Callable[[int, str, Any], None],
|
|
96
|
+
Callable[[int, str, Any], Coroutine[None, None, None]]
|
|
97
|
+
]
|
|
98
|
+
DoorOpenCloseCallback = Union[
|
|
99
|
+
Callable[[int, str, bool], None],
|
|
100
|
+
Callable[[int, str, bool], Coroutine[None, None, None]]
|
|
101
|
+
]
|
|
102
|
+
SensorCallback = Union[
|
|
103
|
+
Callable[[int, str, bool], None],
|
|
104
|
+
Callable[[int, str, bool], Coroutine[None, None, None]]
|
|
105
|
+
]
|
|
106
|
+
SensorStateCallback = Union[
|
|
107
|
+
Callable[[bool], None],
|
|
108
|
+
Callable[[bool], Coroutine[None, None, None]]
|
|
109
|
+
]
|
|
110
|
+
LowBatteryCallback = Union[
|
|
111
|
+
Callable[[int, str], None],
|
|
112
|
+
Callable[[int, str], Coroutine[None, None, None]]
|
|
113
|
+
]
|
|
114
|
+
SensorLowBatteryCallback = Union[
|
|
115
|
+
Callable[[bool], None],
|
|
116
|
+
Callable[[bool], Coroutine[None, None, None]]
|
|
117
|
+
]
|
|
118
|
+
ArmDisarmCallback = Union[
|
|
119
|
+
Callable[[G90ArmDisarmTypes], None],
|
|
120
|
+
Callable[[G90ArmDisarmTypes], Coroutine[None, None, None]]
|
|
121
|
+
]
|
|
122
|
+
|
|
79
123
|
|
|
80
124
|
# pylint: disable=too-many-public-methods
|
|
81
125
|
class G90Alarm(G90DeviceNotifications):
|
|
@@ -83,121 +127,117 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
83
127
|
"""
|
|
84
128
|
Allows to interact with G90 alarm panel.
|
|
85
129
|
|
|
86
|
-
:param
|
|
130
|
+
:param host: Hostname or IP address of the alarm panel. Since the
|
|
87
131
|
protocol is UDP-based it is ok to use broadcast or directed broadcast
|
|
88
132
|
addresses, such as `255.255.255.255` or `10.10.10.255` (the latter assumes
|
|
89
133
|
the device is on `10.10.10.0/24` network)
|
|
90
134
|
:param port: The UDP port of the device where it listens for the
|
|
91
135
|
protocol commands on WiFi interface, currently the devices don't allow it
|
|
92
136
|
to be customized
|
|
93
|
-
:type port: int, optional
|
|
94
137
|
:param reset_occupancy_interval: The interval upon that the sensors are
|
|
95
138
|
simulated to go into inactive state.
|
|
96
|
-
:type reset_occupancy_interval: int, optional
|
|
97
139
|
"""
|
|
98
140
|
# pylint: disable=too-many-instance-attributes,too-many-arguments
|
|
99
|
-
def __init__(self, host, port=REMOTE_PORT,
|
|
100
|
-
reset_occupancy_interval=3,
|
|
101
|
-
notifications_host='0.0.0.0',
|
|
102
|
-
notifications_port=NOTIFICATIONS_PORT):
|
|
141
|
+
def __init__(self, host: str, port: int = REMOTE_PORT,
|
|
142
|
+
reset_occupancy_interval: float = 3.0,
|
|
143
|
+
notifications_host: str = '0.0.0.0',
|
|
144
|
+
notifications_port: int = NOTIFICATIONS_PORT):
|
|
103
145
|
super().__init__(host=notifications_host, port=notifications_port)
|
|
104
146
|
self._host = host
|
|
105
147
|
self._port = port
|
|
106
|
-
self._sensors = []
|
|
107
|
-
self._devices = []
|
|
108
|
-
self._notifications = None
|
|
109
|
-
self._sensor_cb = None
|
|
110
|
-
self._armdisarm_cb = None
|
|
111
|
-
self._door_open_close_cb = None
|
|
112
|
-
self._alarm_cb = None
|
|
113
|
-
self._low_battery_cb = None
|
|
148
|
+
self._sensors: List[G90Sensor] = []
|
|
149
|
+
self._devices: List[G90Device] = []
|
|
150
|
+
self._notifications: Optional[G90DeviceNotifications] = None
|
|
151
|
+
self._sensor_cb: Optional[SensorCallback] = None
|
|
152
|
+
self._armdisarm_cb: Optional[ArmDisarmCallback] = None
|
|
153
|
+
self._door_open_close_cb: Optional[DoorOpenCloseCallback] = None
|
|
154
|
+
self._alarm_cb: Optional[AlarmCallback] = None
|
|
155
|
+
self._low_battery_cb: Optional[LowBatteryCallback] = None
|
|
114
156
|
self._reset_occupancy_interval = reset_occupancy_interval
|
|
115
|
-
self._alert_config = None
|
|
157
|
+
self._alert_config: Optional[G90AlertConfigFlags] = None
|
|
116
158
|
self._sms_alert_when_armed = False
|
|
117
|
-
self._alert_simulation_task = None
|
|
159
|
+
self._alert_simulation_task: Optional[Task[Any]] = None
|
|
118
160
|
self._alert_simulation_start_listener_back = False
|
|
119
161
|
|
|
120
|
-
async def command(
|
|
162
|
+
async def command(
|
|
163
|
+
self, code: G90Commands, data: Optional[G90BaseCommandData] = None
|
|
164
|
+
) -> G90BaseCommandData:
|
|
121
165
|
"""
|
|
122
166
|
Invokes a command against the alarm panel.
|
|
123
167
|
|
|
124
168
|
:param code: Command code
|
|
125
|
-
:type code: :class:`.G90Commands`
|
|
126
169
|
:param data: Command data
|
|
127
|
-
:
|
|
128
|
-
:return: :attr:`.G90BaseCommand.result` that contains the result of
|
|
129
|
-
command invocation
|
|
170
|
+
:return: The result of command invocation
|
|
130
171
|
"""
|
|
131
|
-
cmd = await G90BaseCommand(
|
|
172
|
+
cmd: G90BaseCommand = await G90BaseCommand(
|
|
132
173
|
self._host, self._port, code, data).process()
|
|
133
174
|
return cmd.result
|
|
134
175
|
|
|
135
|
-
def paginated_result(
|
|
176
|
+
def paginated_result(
|
|
177
|
+
self, code: G90Commands, start: int = 1, end: Optional[int] = None
|
|
178
|
+
) -> AsyncGenerator[G90PaginatedResponse, None]:
|
|
136
179
|
"""
|
|
137
180
|
Returns asynchronous generator for a paginated command, that is -
|
|
138
181
|
command operating on a range of records.
|
|
139
182
|
|
|
140
183
|
:param code: Command code
|
|
141
|
-
:
|
|
142
|
-
:param
|
|
143
|
-
:
|
|
144
|
-
:return: :class:`.G90PaginatedResult` being asynchronous generator
|
|
145
|
-
over the result of command invocation. Each access to the generator
|
|
146
|
-
yields :class:`.G90PaginatedResponse` instance
|
|
184
|
+
:param start: Starting record position (one-based)
|
|
185
|
+
:param end: Ending record position (one-based)
|
|
186
|
+
:return: Asynchronous generator over the result of command invocation.
|
|
147
187
|
"""
|
|
148
188
|
return G90PaginatedResult(
|
|
149
189
|
self._host, self._port, code, start, end
|
|
150
190
|
).process()
|
|
151
191
|
|
|
152
192
|
@classmethod
|
|
153
|
-
async def discover(cls):
|
|
193
|
+
async def discover(cls) -> List[G90DiscoveredDevice]:
|
|
154
194
|
"""
|
|
155
195
|
Initiates discovering devices available in the same network segment, by
|
|
156
196
|
using global broadcast address as the destination.
|
|
157
197
|
|
|
158
198
|
:return: List of discovered devices
|
|
159
|
-
:rtype: list[{'guid', 'host', 'port'}]
|
|
160
199
|
"""
|
|
161
|
-
|
|
200
|
+
cmd: G90Discovery = await G90Discovery(
|
|
162
201
|
port=REMOTE_PORT,
|
|
163
202
|
host='255.255.255.255'
|
|
164
203
|
).process()
|
|
204
|
+
return cmd.devices
|
|
165
205
|
|
|
166
206
|
@classmethod
|
|
167
|
-
async def targeted_discover(
|
|
207
|
+
async def targeted_discover(
|
|
208
|
+
cls, device_id: str
|
|
209
|
+
) -> List[G90DiscoveredDeviceTargeted]:
|
|
168
210
|
"""
|
|
169
211
|
Initiates discovering devices available in the same network segment
|
|
170
212
|
using targeted protocol, that is - specifying target device GUID in the
|
|
171
213
|
request, so only the specific device should respond to the query.
|
|
172
214
|
|
|
173
215
|
:param device_id: GUID of the target device to discover
|
|
174
|
-
:type device_id: str
|
|
175
216
|
:return: List of discovered devices
|
|
176
|
-
:rtype: list[{'guid', 'host', 'port'}]
|
|
177
217
|
"""
|
|
178
|
-
|
|
218
|
+
cmd = await G90TargetedDiscovery(
|
|
179
219
|
device_id=device_id,
|
|
180
220
|
port=REMOTE_TARGETED_DISCOVERY_PORT,
|
|
181
221
|
local_port=LOCAL_TARGETED_DISCOVERY_PORT,
|
|
182
222
|
host='255.255.255.255'
|
|
183
223
|
).process()
|
|
224
|
+
return cmd.devices
|
|
184
225
|
|
|
185
226
|
@property
|
|
186
|
-
async def sensors(self):
|
|
227
|
+
async def sensors(self) -> List[G90Sensor]:
|
|
187
228
|
"""
|
|
188
229
|
Property over new :meth:`.get_sensors` method, retained for
|
|
189
230
|
compatibility.
|
|
190
231
|
"""
|
|
191
232
|
return await self.get_sensors()
|
|
192
233
|
|
|
193
|
-
async def get_sensors(self):
|
|
234
|
+
async def get_sensors(self) -> List[G90Sensor]:
|
|
194
235
|
"""
|
|
195
236
|
Provides list of sensors configured in the device. Please note the list
|
|
196
237
|
is cached upon first call, so you need to re-instantiate the class to
|
|
197
238
|
reflect any updates there.
|
|
198
239
|
|
|
199
240
|
:return: List of sensors
|
|
200
|
-
:rtype: list(:class:`.G90Sensor`)
|
|
201
241
|
"""
|
|
202
242
|
if not self._sensors:
|
|
203
243
|
sensors = self.paginated_result(
|
|
@@ -214,14 +254,13 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
214
254
|
|
|
215
255
|
return self._sensors
|
|
216
256
|
|
|
217
|
-
async def find_sensor(self, idx, name):
|
|
257
|
+
async def find_sensor(self, idx: int, name: str) -> Optional[G90Sensor]:
|
|
218
258
|
"""
|
|
219
259
|
Finds sensor by index and name.
|
|
220
260
|
|
|
221
|
-
:param
|
|
222
|
-
:param
|
|
261
|
+
:param idx: Sensor index
|
|
262
|
+
:param name: Sensor name
|
|
223
263
|
:return: Sensor instance
|
|
224
|
-
:rtype :class:`.G90Sensor`|None
|
|
225
264
|
"""
|
|
226
265
|
sensors = await self.get_sensors()
|
|
227
266
|
|
|
@@ -241,14 +280,14 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
241
280
|
return None
|
|
242
281
|
|
|
243
282
|
@property
|
|
244
|
-
async def devices(self):
|
|
283
|
+
async def devices(self) -> List[G90Device]:
|
|
245
284
|
"""
|
|
246
285
|
Property over new :meth:`.get_devices` method, retained for
|
|
247
286
|
compatibility.
|
|
248
287
|
"""
|
|
249
288
|
return await self.get_devices()
|
|
250
289
|
|
|
251
|
-
async def get_devices(self):
|
|
290
|
+
async def get_devices(self) -> List[G90Device]:
|
|
252
291
|
"""
|
|
253
292
|
Provides list of devices (switches) configured in the device. Please
|
|
254
293
|
note the list is cached upon first call, so you need to re-instantiate
|
|
@@ -257,7 +296,6 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
257
296
|
resulting entries.
|
|
258
297
|
|
|
259
298
|
:return: List of devices
|
|
260
|
-
:rtype: list(:class:`.G90Device`)
|
|
261
299
|
"""
|
|
262
300
|
if not self._devices:
|
|
263
301
|
devices = self.paginated_result(
|
|
@@ -283,82 +321,74 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
283
321
|
return self._devices
|
|
284
322
|
|
|
285
323
|
@property
|
|
286
|
-
async def host_info(self):
|
|
324
|
+
async def host_info(self) -> G90HostInfo:
|
|
287
325
|
"""
|
|
288
326
|
Property over new :meth:`.get_host_info` method, retained for
|
|
289
327
|
compatibility.
|
|
290
328
|
"""
|
|
291
329
|
return await self.get_host_info()
|
|
292
330
|
|
|
293
|
-
async def get_host_info(self):
|
|
331
|
+
async def get_host_info(self) -> G90HostInfo:
|
|
294
332
|
"""
|
|
295
333
|
Provides the device information (for example hardware versions, signal
|
|
296
334
|
levels etc.).
|
|
297
335
|
|
|
298
336
|
:return: Device information
|
|
299
|
-
:rtype: Instance of :class:`.G90HostInfo`
|
|
300
337
|
"""
|
|
301
338
|
res = await self.command(G90Commands.GETHOSTINFO)
|
|
302
339
|
return G90HostInfo(*res)
|
|
303
340
|
|
|
304
341
|
@property
|
|
305
|
-
async def host_status(self):
|
|
342
|
+
async def host_status(self) -> G90HostStatus:
|
|
306
343
|
"""
|
|
307
344
|
Property over new :meth:`.get_host_status` method, retained for
|
|
308
345
|
compatibility.
|
|
309
346
|
"""
|
|
310
347
|
return await self.get_host_status()
|
|
311
348
|
|
|
312
|
-
async def get_host_status(self):
|
|
349
|
+
async def get_host_status(self) -> G90HostStatus:
|
|
313
350
|
"""
|
|
314
351
|
Provides the device status (for example, armed or disarmed, configured
|
|
315
352
|
phone number, product name etc.).
|
|
316
353
|
|
|
317
354
|
:return: Device information
|
|
318
|
-
:rtype: Instance of :class:`.G90HostStatus`
|
|
319
355
|
|
|
320
356
|
"""
|
|
321
357
|
res = await self.command(G90Commands.GETHOSTSTATUS)
|
|
322
358
|
return G90HostStatus(*res)
|
|
323
359
|
|
|
324
360
|
@property
|
|
325
|
-
async def alert_config(self):
|
|
361
|
+
async def alert_config(self) -> G90AlertConfigFlags:
|
|
326
362
|
"""
|
|
327
363
|
Property over new :meth:`.get_alert_config` method, retained for
|
|
328
364
|
compatibility.
|
|
329
365
|
"""
|
|
330
366
|
return await self.get_alert_config()
|
|
331
367
|
|
|
332
|
-
async def get_alert_config(self):
|
|
368
|
+
async def get_alert_config(self) -> G90AlertConfigFlags:
|
|
333
369
|
"""
|
|
334
370
|
Retrieves the alert configuration flags from the device. Please note
|
|
335
371
|
the configuration is cached upon first call, so you need to
|
|
336
372
|
re-instantiate the class to reflect any updates there.
|
|
337
373
|
|
|
338
|
-
:return:
|
|
339
|
-
alerts configured
|
|
374
|
+
:return: The alerts configured
|
|
340
375
|
"""
|
|
341
376
|
if not self._alert_config:
|
|
342
377
|
self._alert_config = await self._alert_config_uncached()
|
|
343
378
|
return self._alert_config
|
|
344
379
|
|
|
345
|
-
async def _alert_config_uncached(self):
|
|
380
|
+
async def _alert_config_uncached(self) -> G90AlertConfigFlags:
|
|
346
381
|
"""
|
|
347
382
|
Retrieves the alert configuration flags directly from the device.
|
|
348
383
|
|
|
349
|
-
:return:
|
|
350
|
-
alerts configured
|
|
384
|
+
:return: The alerts configured
|
|
351
385
|
"""
|
|
352
386
|
res = await self.command(G90Commands.GETNOTICEFLAG)
|
|
353
387
|
return G90AlertConfig(*res).flags
|
|
354
388
|
|
|
355
|
-
async def set_alert_config(self,
|
|
389
|
+
async def set_alert_config(self, flags: G90AlertConfigFlags) -> None:
|
|
356
390
|
"""
|
|
357
|
-
|
|
358
|
-
`async_as_sync` decorator, although it might have implications with the
|
|
359
|
-
setter not executed if the program terminates earlier. Hence, for the
|
|
360
|
-
sake of better predictability this is implemented as regular
|
|
361
|
-
(non-property) method
|
|
391
|
+
Sets the alert configuration flags on the device.
|
|
362
392
|
"""
|
|
363
393
|
# Use uncached method retrieving the alert configuration, to ensure the
|
|
364
394
|
# actual value retrieved from the device
|
|
@@ -367,45 +397,43 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
367
397
|
_LOGGER.warning(
|
|
368
398
|
'Alert configuration changed externally,'
|
|
369
399
|
' overwriting (read "%s", will be set to "%s")',
|
|
370
|
-
str(alert_config), str(
|
|
400
|
+
str(alert_config), str(flags)
|
|
371
401
|
)
|
|
372
|
-
await self.command(G90Commands.SETNOTICEFLAG, [value])
|
|
402
|
+
await self.command(G90Commands.SETNOTICEFLAG, [flags.value])
|
|
373
403
|
# Update the alert configuration stored
|
|
374
|
-
self._alert_config =
|
|
404
|
+
self._alert_config = flags
|
|
375
405
|
|
|
376
406
|
@property
|
|
377
|
-
async def user_data_crc(self):
|
|
407
|
+
async def user_data_crc(self) -> G90UserDataCRC:
|
|
378
408
|
"""
|
|
379
409
|
Property over new :meth:`.get_user_data_crc` method, retained for
|
|
380
410
|
compatibility.
|
|
381
411
|
"""
|
|
382
412
|
return await self.get_user_data_crc()
|
|
383
413
|
|
|
384
|
-
async def get_user_data_crc(self):
|
|
414
|
+
async def get_user_data_crc(self) -> G90UserDataCRC:
|
|
385
415
|
"""
|
|
386
416
|
Retieves checksums (CRC) for different on-device databases (history,
|
|
387
417
|
sensors etc.). Might be used to detect if there is a change in a
|
|
388
418
|
particular database.
|
|
389
419
|
|
|
390
|
-
.. note:: Note that due to a bug in the firmware CRC for
|
|
420
|
+
.. note:: Note that due to a bug in the firmware CRC for sensors and
|
|
391
421
|
device databases change on each call even if there were no changes
|
|
392
422
|
|
|
393
|
-
:return:
|
|
394
|
-
different databases
|
|
423
|
+
:return: Checksums for different databases
|
|
395
424
|
"""
|
|
396
425
|
res = await self.command(G90Commands.GETUSERDATACRC)
|
|
397
426
|
return G90UserDataCRC(*res)
|
|
398
427
|
|
|
399
|
-
async def history(
|
|
428
|
+
async def history(
|
|
429
|
+
self, start: int = 1, count: int = 1
|
|
430
|
+
) -> List[G90History]:
|
|
400
431
|
"""
|
|
401
432
|
Retrieves event history from the device.
|
|
402
433
|
|
|
403
434
|
:param start: Starting record number (one-based)
|
|
404
|
-
:type start: int
|
|
405
435
|
:param count: Number of records to retrieve
|
|
406
|
-
:type count: int
|
|
407
436
|
:return: List of history entries
|
|
408
|
-
:rtype: list[:class:`.G90History`]
|
|
409
437
|
"""
|
|
410
438
|
res = self.paginated_result(G90Commands.GETHISTORY,
|
|
411
439
|
start, count)
|
|
@@ -417,18 +445,23 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
417
445
|
key=lambda x: x.datetime, reverse=True
|
|
418
446
|
)
|
|
419
447
|
|
|
420
|
-
async def on_sensor_activity(
|
|
448
|
+
async def on_sensor_activity(
|
|
449
|
+
self, idx: int, name: str, occupancy: bool = True
|
|
450
|
+
) -> None:
|
|
421
451
|
"""
|
|
422
|
-
|
|
423
|
-
alerts, since the logic for both is same and could be reused.
|
|
424
|
-
|
|
452
|
+
Invoked both for sensor notifications and door open/close
|
|
453
|
+
alerts, since the logic for both is same and could be reused.
|
|
454
|
+
Fires corresponding callback if set by the user with
|
|
455
|
+
:attr:`.sensor_callback`.
|
|
456
|
+
|
|
457
|
+
Please note the method is for internal use by the class.
|
|
425
458
|
|
|
426
|
-
:param
|
|
459
|
+
:param idx: The index of the sensor the callback is invoked for.
|
|
427
460
|
Please note the index is a property of sensor, not the direct index of
|
|
428
461
|
:attr:`sensors` array
|
|
429
|
-
:param
|
|
462
|
+
:param name: The name of the sensor, along with the `idx` parameter
|
|
430
463
|
it is used to look the sensor up from the :attr:`sensors` list
|
|
431
|
-
:param
|
|
464
|
+
:param occupancy: The flag indicating the target sensor state
|
|
432
465
|
(=occupancy), will always be `True` for callbacks invoked from alarm
|
|
433
466
|
panel notifications, and reflects actual sensor state for device
|
|
434
467
|
alerts (only for `door` type sensors, if door open/close alerts are
|
|
@@ -444,7 +477,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
444
477
|
# Emulate turning off the occupancy - most of sensors will not
|
|
445
478
|
# notify the device of that, nor the device would emit such
|
|
446
479
|
# notification itself
|
|
447
|
-
def reset_sensor_occupancy(sensor):
|
|
480
|
+
def reset_sensor_occupancy(sensor: G90Sensor) -> None:
|
|
448
481
|
_LOGGER.debug('Resetting occupancy for sensor %s', sensor)
|
|
449
482
|
sensor.occupancy = False
|
|
450
483
|
G90Callback.invoke(sensor.state_callback, sensor.occupancy)
|
|
@@ -479,25 +512,27 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
479
512
|
G90Callback.invoke(self._sensor_cb, idx, name, occupancy)
|
|
480
513
|
|
|
481
514
|
@property
|
|
482
|
-
def sensor_callback(self):
|
|
515
|
+
def sensor_callback(self) -> Optional[SensorCallback]:
|
|
483
516
|
"""
|
|
484
|
-
|
|
485
|
-
sensor activates.
|
|
486
|
-
|
|
487
|
-
:type: .. py:function:: ()(idx, name, occupancy)
|
|
517
|
+
Sensor activity callback, which is invoked when sensor activates.
|
|
488
518
|
"""
|
|
489
519
|
return self._sensor_cb
|
|
490
520
|
|
|
491
521
|
@sensor_callback.setter
|
|
492
|
-
def sensor_callback(self, value):
|
|
522
|
+
def sensor_callback(self, value: SensorCallback) -> None:
|
|
493
523
|
self._sensor_cb = value
|
|
494
524
|
|
|
495
|
-
async def on_door_open_close(
|
|
525
|
+
async def on_door_open_close(
|
|
526
|
+
self, event_id: int, zone_name: str, is_open: bool
|
|
527
|
+
) -> None:
|
|
496
528
|
"""
|
|
497
|
-
|
|
498
|
-
panel.
|
|
529
|
+
Invoked when door open/close alert comes from the alarm
|
|
530
|
+
panel. Fires corresponding callback if set by the user with
|
|
531
|
+
:attr:`.door_open_close_callback`.
|
|
532
|
+
|
|
533
|
+
Please note the method is for internal use by the class.
|
|
499
534
|
|
|
500
|
-
.. seealso:: `method
|
|
535
|
+
.. seealso:: :meth:`.on_sensor_activity` method for arguments
|
|
501
536
|
"""
|
|
502
537
|
# Same internal callback is reused both for door open/close alerts and
|
|
503
538
|
# sensor notifications. The former adds reporting when a door is
|
|
@@ -509,30 +544,26 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
509
544
|
)
|
|
510
545
|
|
|
511
546
|
@property
|
|
512
|
-
def door_open_close_callback(self):
|
|
547
|
+
def door_open_close_callback(self) -> Optional[DoorOpenCloseCallback]:
|
|
513
548
|
"""
|
|
514
|
-
|
|
549
|
+
The door open/close callback, which is invoked when door
|
|
515
550
|
is opened or closed (if corresponding alert is configured on the
|
|
516
551
|
device).
|
|
517
|
-
|
|
518
|
-
:type: .. py:function:: ()(idx: int, name: str, is_open: bool)
|
|
519
552
|
"""
|
|
520
553
|
return self._door_open_close_cb
|
|
521
554
|
|
|
522
555
|
@door_open_close_callback.setter
|
|
523
|
-
def door_open_close_callback(self, value):
|
|
524
|
-
"""
|
|
525
|
-
Sets callback for door open/close events.
|
|
526
|
-
"""
|
|
556
|
+
def door_open_close_callback(self, value: DoorOpenCloseCallback) -> None:
|
|
527
557
|
self._door_open_close_cb = value
|
|
528
558
|
|
|
529
|
-
async def on_armdisarm(self, state):
|
|
559
|
+
async def on_armdisarm(self, state: G90ArmDisarmTypes) -> None:
|
|
530
560
|
"""
|
|
531
|
-
|
|
532
|
-
|
|
561
|
+
Invoked when the device is armed or disarmed. Fires corresponding
|
|
562
|
+
callback if set by the user with :attr:`.armdisarm_callback`.
|
|
563
|
+
|
|
564
|
+
Please note the method is for internal use by the class.
|
|
533
565
|
|
|
534
566
|
:param state: Device state (armed, disarmed, armed home)
|
|
535
|
-
:type state: :class:`G90ArmDisarmTypes`
|
|
536
567
|
"""
|
|
537
568
|
if self._sms_alert_when_armed:
|
|
538
569
|
if state == G90ArmDisarmTypes.DISARM:
|
|
@@ -549,27 +580,26 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
549
580
|
G90Callback.invoke(self._armdisarm_cb, state)
|
|
550
581
|
|
|
551
582
|
@property
|
|
552
|
-
def armdisarm_callback(self):
|
|
583
|
+
def armdisarm_callback(self) -> Optional[ArmDisarmCallback]:
|
|
553
584
|
"""
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
:type: .. py:function:: ()(state: :class:`.G90ArmDisarmTypes`)
|
|
585
|
+
The device arm/disarm callback, which is invoked when device state
|
|
586
|
+
changes.
|
|
558
587
|
"""
|
|
559
588
|
return self._armdisarm_cb
|
|
560
589
|
|
|
561
590
|
@armdisarm_callback.setter
|
|
562
|
-
def armdisarm_callback(self, value):
|
|
591
|
+
def armdisarm_callback(self, value: ArmDisarmCallback) -> None:
|
|
563
592
|
self._armdisarm_cb = value
|
|
564
593
|
|
|
565
|
-
async def on_alarm(self, event_id, zone_name):
|
|
594
|
+
async def on_alarm(self, event_id: int, zone_name: str) -> None:
|
|
566
595
|
"""
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
596
|
+
Invoked when alarm is triggered. Fires corresponding callback if set by
|
|
597
|
+
the user with :attr:`.alarm_callback`.
|
|
598
|
+
|
|
599
|
+
Please note the method is for internal use by the class.
|
|
570
600
|
|
|
571
|
-
:param
|
|
572
|
-
:param
|
|
601
|
+
:param event_id: Index of the sensor triggered alarm
|
|
602
|
+
:param zone_name: Sensor name
|
|
573
603
|
"""
|
|
574
604
|
sensor = await self.find_sensor(event_id, zone_name)
|
|
575
605
|
# The callback is still delivered to the caller even if the sensor
|
|
@@ -588,31 +618,26 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
588
618
|
)
|
|
589
619
|
|
|
590
620
|
@property
|
|
591
|
-
def alarm_callback(self):
|
|
621
|
+
def alarm_callback(self) -> Optional[AlarmCallback]:
|
|
592
622
|
"""
|
|
593
|
-
|
|
594
|
-
device alarm triggers.
|
|
595
|
-
|
|
596
|
-
:type:
|
|
597
|
-
.. py:function:: ()(
|
|
598
|
-
sensor_idx: int, sensor_name: str, extra_data: str|None
|
|
599
|
-
)
|
|
623
|
+
The device alarm callback, which is invoked when device alarm triggers.
|
|
600
624
|
"""
|
|
601
625
|
return self._alarm_cb
|
|
602
626
|
|
|
603
627
|
@alarm_callback.setter
|
|
604
|
-
def alarm_callback(self, value):
|
|
628
|
+
def alarm_callback(self, value: AlarmCallback) -> None:
|
|
605
629
|
self._alarm_cb = value
|
|
606
630
|
|
|
607
|
-
async def on_low_battery(self, event_id, zone_name):
|
|
631
|
+
async def on_low_battery(self, event_id: int, zone_name: str) -> None:
|
|
608
632
|
"""
|
|
609
|
-
|
|
633
|
+
Invoked when the sensor reports on low battery. Fires
|
|
610
634
|
corresponding callback if set by the user with
|
|
611
|
-
|
|
612
|
-
|
|
635
|
+
:attr:`.on_low_battery_callback`.
|
|
636
|
+
|
|
637
|
+
Please note the method is for internal use by the class.
|
|
613
638
|
|
|
614
|
-
:param
|
|
615
|
-
:param
|
|
639
|
+
:param event_id: Index of the sensor triggered alarm
|
|
640
|
+
:param zone_name: Sensor name
|
|
616
641
|
"""
|
|
617
642
|
sensor = await self.find_sensor(event_id, zone_name)
|
|
618
643
|
if sensor:
|
|
@@ -622,33 +647,30 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
622
647
|
G90Callback.invoke(self._low_battery_cb, event_id, zone_name)
|
|
623
648
|
|
|
624
649
|
@property
|
|
625
|
-
def low_battery_callback(self):
|
|
650
|
+
def low_battery_callback(self) -> Optional[LowBatteryCallback]:
|
|
626
651
|
"""
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
:type: .. py:function:: ()(idx, name)
|
|
652
|
+
Low battery callback, which is invoked when sensor reports the
|
|
653
|
+
condition.
|
|
631
654
|
"""
|
|
632
655
|
return self._low_battery_cb
|
|
633
656
|
|
|
634
657
|
@low_battery_callback.setter
|
|
635
|
-
def low_battery_callback(self, value):
|
|
658
|
+
def low_battery_callback(self, value: LowBatteryCallback) -> None:
|
|
636
659
|
self._low_battery_cb = value
|
|
637
660
|
|
|
638
|
-
async def listen_device_notifications(self):
|
|
661
|
+
async def listen_device_notifications(self) -> None:
|
|
639
662
|
"""
|
|
640
663
|
Starts internal listener for device notifications/alerts.
|
|
641
|
-
|
|
642
664
|
"""
|
|
643
665
|
await self.listen()
|
|
644
666
|
|
|
645
|
-
def close_device_notifications(self):
|
|
667
|
+
def close_device_notifications(self) -> None:
|
|
646
668
|
"""
|
|
647
669
|
Closes the listener for device notifications/alerts.
|
|
648
670
|
"""
|
|
649
671
|
self.close()
|
|
650
672
|
|
|
651
|
-
async def arm_away(self):
|
|
673
|
+
async def arm_away(self) -> None:
|
|
652
674
|
"""
|
|
653
675
|
Arms the device in away mode.
|
|
654
676
|
"""
|
|
@@ -656,7 +678,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
656
678
|
await self.command(G90Commands.SETHOSTSTATUS,
|
|
657
679
|
[state])
|
|
658
680
|
|
|
659
|
-
async def arm_home(self):
|
|
681
|
+
async def arm_home(self) -> None:
|
|
660
682
|
"""
|
|
661
683
|
Arms the device in home mode.
|
|
662
684
|
"""
|
|
@@ -664,7 +686,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
664
686
|
await self.command(G90Commands.SETHOSTSTATUS,
|
|
665
687
|
[state])
|
|
666
688
|
|
|
667
|
-
async def disarm(self):
|
|
689
|
+
async def disarm(self) -> None:
|
|
668
690
|
"""
|
|
669
691
|
Disarms the device.
|
|
670
692
|
"""
|
|
@@ -673,7 +695,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
673
695
|
[state])
|
|
674
696
|
|
|
675
697
|
@property
|
|
676
|
-
def sms_alert_when_armed(self):
|
|
698
|
+
def sms_alert_when_armed(self) -> bool:
|
|
677
699
|
"""
|
|
678
700
|
When enabled, allows to save costs on SMS by having corresponding alert
|
|
679
701
|
enabled only when device is armed.
|
|
@@ -681,12 +703,12 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
681
703
|
return self._sms_alert_when_armed
|
|
682
704
|
|
|
683
705
|
@sms_alert_when_armed.setter
|
|
684
|
-
def sms_alert_when_armed(self, value):
|
|
706
|
+
def sms_alert_when_armed(self, value: bool) -> None:
|
|
685
707
|
self._sms_alert_when_armed = value
|
|
686
708
|
|
|
687
709
|
async def start_simulating_alerts_from_history(
|
|
688
|
-
self, interval=5, history_depth=5
|
|
689
|
-
):
|
|
710
|
+
self, interval: float = 5, history_depth: int = 5
|
|
711
|
+
) -> None:
|
|
690
712
|
"""
|
|
691
713
|
Starts the separate task to simulate device alerts from history
|
|
692
714
|
entries.
|
|
@@ -695,9 +717,9 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
695
717
|
notifications will not be processed thus resulting in possible
|
|
696
718
|
duplicated if those could be received from the network.
|
|
697
719
|
|
|
698
|
-
:param
|
|
720
|
+
:param interval: Interval (in seconds) between polling for newer
|
|
699
721
|
history entities
|
|
700
|
-
:param
|
|
722
|
+
:param history_depth: Amount of history entries to fetch during
|
|
701
723
|
each polling cycle
|
|
702
724
|
"""
|
|
703
725
|
# Remember if device notifications listener has been started already
|
|
@@ -710,7 +732,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
710
732
|
self._simulate_alerts_from_history(interval, history_depth)
|
|
711
733
|
)
|
|
712
734
|
|
|
713
|
-
async def stop_simulating_alerts_from_history(self):
|
|
735
|
+
async def stop_simulating_alerts_from_history(self) -> None:
|
|
714
736
|
"""
|
|
715
737
|
Stops the task simulating device alerts from history entries.
|
|
716
738
|
|
|
@@ -728,7 +750,9 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
728
750
|
if self._alert_simulation_start_listener_back:
|
|
729
751
|
await self.listen()
|
|
730
752
|
|
|
731
|
-
async def _simulate_alerts_from_history(
|
|
753
|
+
async def _simulate_alerts_from_history(
|
|
754
|
+
self, interval: float, history_depth: int
|
|
755
|
+
) -> None:
|
|
732
756
|
"""
|
|
733
757
|
Periodically fetches history entries from the device and simulates
|
|
734
758
|
device alerts off of those.
|
|
@@ -736,7 +760,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
736
760
|
Only the history entries occur after the process is started are
|
|
737
761
|
handled, to avoid triggering callbacks retrospectively.
|
|
738
762
|
|
|
739
|
-
See :
|
|
763
|
+
See :meth:`.start_simulating_alerts_from_history` for the parameters.
|
|
740
764
|
"""
|
|
741
765
|
last_history_ts = None
|
|
742
766
|
|
|
@@ -773,7 +797,7 @@ class G90Alarm(G90DeviceNotifications):
|
|
|
773
797
|
for item in reversed(history):
|
|
774
798
|
# Process only the entries newer than one been recorded as
|
|
775
799
|
# most recent one
|
|
776
|
-
if item.datetime > last_history_ts:
|
|
800
|
+
if last_history_ts and item.datetime > last_history_ts:
|
|
777
801
|
_LOGGER.debug(
|
|
778
802
|
'Found newer history entry: %s, simulating alert',
|
|
779
803
|
repr(item)
|