pyg90alarm 1.10.1__py3-none-any.whl → 1.13.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.
@@ -475,6 +475,16 @@ SENSOR_DEFINITIONS = [
475
475
  rwMode=SensorRwMode.READ,
476
476
  matchMode=SensorMatchMode.ONLY20BITS
477
477
  ),
478
+ # Door Sensor WRDS01
479
+ SensorDefinition(
480
+ type=1,
481
+ subtype=3,
482
+ rx=0,
483
+ tx=0,
484
+ private_data='00',
485
+ rwMode=SensorRwMode.READ,
486
+ matchMode=SensorMatchMode.ALL
487
+ ),
478
488
  # Door Sensor
479
489
  SensorDefinition(
480
490
  type=1,
@@ -485,6 +495,96 @@ SENSOR_DEFINITIONS = [
485
495
  rwMode=SensorRwMode.READ,
486
496
  matchMode=SensorMatchMode.ONLY16BITS
487
497
  ),
498
+ # Glass Break Sensor BLPS
499
+ SensorDefinition(
500
+ type=2,
501
+ subtype=0,
502
+ rx=0,
503
+ tx=0,
504
+ private_data='00',
505
+ rwMode=SensorRwMode.READ,
506
+ matchMode=SensorMatchMode.ALL
507
+ ),
508
+ # Gas Detector WGD01
509
+ SensorDefinition(
510
+ type=3,
511
+ subtype=0,
512
+ rx=0,
513
+ tx=0,
514
+ private_data='00',
515
+ rwMode=SensorRwMode.READ,
516
+ matchMode=SensorMatchMode.ALL
517
+ ),
518
+ # Smoke Detector WSD02
519
+ SensorDefinition(
520
+ type=4,
521
+ subtype=0,
522
+ rx=0,
523
+ tx=0,
524
+ private_data='00',
525
+ rwMode=SensorRwMode.READ,
526
+ matchMode=SensorMatchMode.ALL
527
+ ),
528
+ # Smoke Detector WSD04
529
+ SensorDefinition(
530
+ type=4,
531
+ subtype=1,
532
+ rx=0,
533
+ tx=0,
534
+ private_data='00',
535
+ rwMode=SensorRwMode.READ,
536
+ matchMode=SensorMatchMode.ALL
537
+ ),
538
+ # Panic Button WEB01
539
+ SensorDefinition(
540
+ type=5,
541
+ subtype=1,
542
+ rx=0,
543
+ tx=0,
544
+ private_data='00',
545
+ rwMode=SensorRwMode.READ,
546
+ matchMode=SensorMatchMode.ALL
547
+ ),
548
+ # Panic Button WEB03
549
+ SensorDefinition(
550
+ type=5,
551
+ subtype=0,
552
+ rx=0,
553
+ tx=0,
554
+ private_data='00',
555
+ rwMode=SensorRwMode.READ,
556
+ matchMode=SensorMatchMode.ALL
557
+ ),
558
+ # Shock Sensor WSS01
559
+ SensorDefinition(
560
+ type=6,
561
+ subtype=0,
562
+ rx=0,
563
+ tx=0,
564
+ private_data='00',
565
+ rwMode=SensorRwMode.READ,
566
+ matchMode=SensorMatchMode.ALL
567
+ ),
568
+ # Water Detector LSTC02
569
+ SensorDefinition(
570
+ type=7,
571
+ subtype=1,
572
+ rx=0,
573
+ tx=0,
574
+ private_data='',
575
+ rwMode=SensorRwMode.READ,
576
+ matchMode=SensorMatchMode.ALL
577
+ ),
578
+ # Water Detector LSTC01
579
+ SensorDefinition(
580
+ type=7,
581
+ subtype=0,
582
+ rx=0,
583
+ tx=0,
584
+ private_data='00',
585
+ rwMode=SensorRwMode.READ,
586
+ matchMode=SensorMatchMode.ALL
587
+ ),
488
588
  # PIR motion sensor WMS08
489
589
  SensorDefinition(
490
590
  type=8,
@@ -505,6 +605,66 @@ SENSOR_DEFINITIONS = [
505
605
  rwMode=SensorRwMode.READ,
506
606
  matchMode=SensorMatchMode.ONLY20BITS
507
607
  ),
608
+ # PIR motion sensor ODPIR
609
+ SensorDefinition(
610
+ type=8,
611
+ subtype=0,
612
+ rx=0,
613
+ tx=0,
614
+ private_data='00',
615
+ rwMode=SensorRwMode.READ,
616
+ matchMode=SensorMatchMode.ALL
617
+ ),
618
+ # PIR motion sensor N650
619
+ SensorDefinition(
620
+ type=8,
621
+ subtype=5,
622
+ rx=0,
623
+ tx=0,
624
+ private_data='00',
625
+ rwMode=SensorRwMode.READ,
626
+ matchMode=SensorMatchMode.ALL
627
+ ),
628
+ # PIR motion sensor WPD02
629
+ SensorDefinition(
630
+ type=8,
631
+ subtype=6,
632
+ rx=0,
633
+ tx=0,
634
+ private_data='00',
635
+ rwMode=SensorRwMode.READ,
636
+ matchMode=SensorMatchMode.ALL
637
+ ),
638
+ # PIR motion sensor WCMS02
639
+ SensorDefinition(
640
+ type=8,
641
+ subtype=8,
642
+ rx=0,
643
+ tx=0,
644
+ private_data='00',
645
+ rwMode=SensorRwMode.READ,
646
+ matchMode=SensorMatchMode.ALL
647
+ ),
648
+ # PIR motion sensor CWMS01
649
+ SensorDefinition(
650
+ type=8,
651
+ subtype=9,
652
+ rx=0,
653
+ tx=0,
654
+ private_data='00',
655
+ rwMode=SensorRwMode.READ,
656
+ matchMode=SensorMatchMode.ALL
657
+ ),
658
+ # PIR motion sensor WMS04
659
+ SensorDefinition(
660
+ type=8,
661
+ subtype=11,
662
+ rx=0,
663
+ tx=0,
664
+ private_data='00',
665
+ rwMode=SensorRwMode.READ,
666
+ matchMode=SensorMatchMode.ALL
667
+ ),
508
668
  # PIR motion sensor WMS07
509
669
  SensorDefinition(
510
670
  type=8,
@@ -515,6 +675,66 @@ SENSOR_DEFINITIONS = [
515
675
  rwMode=SensorRwMode.READ,
516
676
  matchMode=SensorMatchMode.ONLY20BITS
517
677
  ),
678
+ # PIR motion sensor ODPIR03
679
+ SensorDefinition(
680
+ type=8,
681
+ subtype=4,
682
+ rx=0,
683
+ tx=0,
684
+ private_data='00',
685
+ rwMode=SensorRwMode.READ,
686
+ matchMode=SensorMatchMode.ALL
687
+ ),
688
+ # PIR motion sensor WPD01
689
+ SensorDefinition(
690
+ type=8,
691
+ subtype=7,
692
+ rx=0,
693
+ tx=0,
694
+ private_data='00',
695
+ rwMode=SensorRwMode.READ,
696
+ matchMode=SensorMatchMode.ALL
697
+ ),
698
+ # PIR motion sensor PIR Ceiling
699
+ SensorDefinition(
700
+ type=8,
701
+ subtype=1,
702
+ rx=0,
703
+ tx=0,
704
+ private_data='00',
705
+ rwMode=SensorRwMode.READ,
706
+ matchMode=SensorMatchMode.ALL
707
+ ),
708
+ # Beams ABT
709
+ SensorDefinition(
710
+ type=9,
711
+ subtype=0,
712
+ rx=0,
713
+ tx=0,
714
+ private_data='00',
715
+ rwMode=SensorRwMode.READ,
716
+ matchMode=SensorMatchMode.ALL
717
+ ),
718
+ # Beams ABE
719
+ SensorDefinition(
720
+ type=9,
721
+ subtype=1,
722
+ rx=0,
723
+ tx=0,
724
+ private_data='00',
725
+ rwMode=SensorRwMode.READ,
726
+ matchMode=SensorMatchMode.ALL
727
+ ),
728
+ # Beams ABH
729
+ SensorDefinition(
730
+ type=9,
731
+ subtype=2,
732
+ rx=0,
733
+ tx=0,
734
+ private_data='00',
735
+ rwMode=SensorRwMode.READ,
736
+ matchMode=SensorMatchMode.ALL
737
+ ),
518
738
  # Remote RMC08
519
739
  SensorDefinition(
520
740
  type=10,
@@ -565,6 +785,16 @@ SENSOR_DEFINITIONS = [
565
785
  rwMode=SensorRwMode.READ,
566
786
  matchMode=SensorMatchMode.ONLY20BITS
567
787
  ),
788
+ # Door Bell WDB
789
+ SensorDefinition(
790
+ type=12,
791
+ subtype=1,
792
+ rx=0,
793
+ tx=0,
794
+ private_data='00',
795
+ rwMode=SensorRwMode.READ,
796
+ matchMode=SensorMatchMode.ALL
797
+ ),
568
798
  # TouchID Detector
569
799
  SensorDefinition(
570
800
  type=13,
@@ -595,7 +825,17 @@ SENSOR_DEFINITIONS = [
595
825
  rwMode=SensorRwMode.READ,
596
826
  matchMode=SensorMatchMode.ONLY16BITS
597
827
  ),
598
- # Gas Detector WGD02
828
+ # Sub Host SS08S
829
+ SensorDefinition(
830
+ type=16,
831
+ subtype=0,
832
+ rx=0,
833
+ tx=0,
834
+ private_data='00',
835
+ rwMode=SensorRwMode.READ,
836
+ matchMode=SensorMatchMode.ALL
837
+ ),
838
+ # Gas Valve Detector WGD02
599
839
  SensorDefinition(
600
840
  type=18,
601
841
  subtype=0,
@@ -21,7 +21,6 @@
21
21
  """
22
22
  Implements support for notifications/alerts sent by G90 alarm panel.
23
23
  """
24
-
25
24
  import json
26
25
  import logging
27
26
  from collections import namedtuple
@@ -34,6 +33,7 @@ from .const import (
34
33
  G90AlertStateChangeTypes,
35
34
  G90ArmDisarmTypes,
36
35
  G90AlertSources,
36
+ G90AlertStates,
37
37
  )
38
38
 
39
39
 
@@ -43,7 +43,7 @@ _LOGGER = logging.getLogger(__name__)
43
43
  class G90Message(namedtuple('G90Message',
44
44
  ['code', 'data'])):
45
45
  """
46
- tbd
46
+ Represents the message received from the device.
47
47
 
48
48
  :meta private:
49
49
  """
@@ -52,7 +52,7 @@ class G90Message(namedtuple('G90Message',
52
52
  class G90Notification(namedtuple('G90Notification',
53
53
  ['kind', 'data'])):
54
54
  """
55
- tbd
55
+ Represents the notification received from the device.
56
56
 
57
57
  :meta private:
58
58
  """
@@ -61,7 +61,7 @@ class G90Notification(namedtuple('G90Notification',
61
61
  class G90ZoneInfo(namedtuple('G90ZoneInfo',
62
62
  ['idx', 'name'])):
63
63
  """
64
- tbd
64
+ Represents zone details received from the device.
65
65
 
66
66
  :meta private:
67
67
  """
@@ -70,7 +70,7 @@ class G90ZoneInfo(namedtuple('G90ZoneInfo',
70
70
  class G90ArmDisarmInfo(namedtuple('G90ArmDisarmInfo',
71
71
  ['state'])):
72
72
  """
73
- tbd
73
+ Represents the arm/disarm state received from the device.
74
74
 
75
75
  :meta private:
76
76
  """
@@ -81,46 +81,31 @@ class G90DeviceAlert(namedtuple('G90DeviceAlert',
81
81
  'zone_name', 'device_id', 'unix_time',
82
82
  'resv4', 'other'])):
83
83
  """
84
- tbd
84
+ Represents alert received from the device.
85
85
 
86
86
  :meta private:
87
87
  """
88
88
 
89
89
 
90
- class G90DeviceNotificationProtocol:
90
+ class G90DeviceNotifications:
91
91
  """
92
92
  tbd
93
-
94
- :meta private:
95
93
  """
96
- def __init__(self, armdisarm_cb=None, sensor_cb=None,
97
- door_open_close_cb=None, alarm_cb=None):
98
- """
99
- tbd
100
- """
101
- self._armdisarm_cb = armdisarm_cb
102
- self._sensor_cb = sensor_cb
103
- self._door_open_close_cb = door_open_close_cb
104
- self._alarm_cb = alarm_cb
105
-
106
- def connection_made(self, transport):
107
- """
108
- tbd
109
- """
110
-
111
- def connection_lost(self, exc):
112
- """
113
- tbd
114
- """
94
+ def __init__(self, port, host):
95
+ # pylint: disable=too-many-arguments
96
+ self._notification_transport = None
97
+ self._notifications_host = host
98
+ self._notifications_port = port
115
99
 
116
100
  def _handle_notification(self, addr, notification):
117
101
  # Sensor activity notification
118
102
  if notification.kind == G90NotificationTypes.SENSOR_ACTIVITY:
119
103
  g90_zone_info = G90ZoneInfo(*notification.data)
120
104
  _LOGGER.debug('Sensor notification: %s', g90_zone_info)
121
- G90Callback.invoke(self._sensor_cb,
122
- g90_zone_info.idx,
123
- g90_zone_info.name)
105
+ G90Callback.invoke(
106
+ self.on_sensor_activity,
107
+ g90_zone_info.idx, g90_zone_info.name
108
+ )
124
109
  return
125
110
 
126
111
  # Arm/disarm notification
@@ -131,8 +116,7 @@ class G90DeviceNotificationProtocol:
131
116
  state = G90ArmDisarmTypes(g90_armdisarm_info.state)
132
117
  _LOGGER.debug('Arm/disarm notification: %s',
133
118
  state)
134
- G90Callback.invoke(self._armdisarm_cb,
135
- state)
119
+ G90Callback.invoke(self.on_armdisarm, state)
136
120
  return
137
121
 
138
122
  _LOGGER.warning('Unknown notification received from %s:%s:'
@@ -141,14 +125,30 @@ class G90DeviceNotificationProtocol:
141
125
 
142
126
  def _handle_alert(self, addr, alert):
143
127
  if alert.type == G90AlertTypes.DOOR_OPEN_CLOSE:
144
- is_open = (
145
- alert.source == G90AlertSources.SENSOR and alert.state == 1
146
- ) or alert.source == G90AlertSources.DOORBELL
147
- _LOGGER.debug('Door open_close alert: %s', alert)
148
- G90Callback.invoke(self._door_open_close_cb,
149
- alert.event_id, alert.zone_name,
150
- is_open)
151
- return
128
+ if alert.state in (
129
+ G90AlertStates.DOOR_OPEN, G90AlertStates.DOOR_CLOSE
130
+ ):
131
+ is_open = (
132
+ alert.source == G90AlertSources.SENSOR
133
+ and alert.state == G90AlertStates.DOOR_OPEN # noqa: W503
134
+ ) or alert.source == G90AlertSources.DOORBELL
135
+ _LOGGER.debug('Door open_close alert: %s', alert)
136
+ G90Callback.invoke(
137
+ self.on_door_open_close,
138
+ alert.event_id, alert.zone_name, is_open
139
+ )
140
+ return
141
+
142
+ if (
143
+ alert.source == G90AlertSources.SENSOR
144
+ and alert.state == G90AlertStates.LOW_BATTERY # noqa: W503
145
+ ):
146
+ _LOGGER.debug('Low battery alert: %s', alert)
147
+ G90Callback.invoke(
148
+ self.on_low_battery,
149
+ alert.event_id, alert.zone_name
150
+ )
151
+ return
152
152
 
153
153
  if alert.type == G90AlertTypes.STATE_CHANGE:
154
154
  # Define the mapping between device state received in the alert, to
@@ -168,21 +168,36 @@ class G90DeviceNotificationProtocol:
168
168
  # We received the device state change related to arm/disarm,
169
169
  # invoke the corresponding callback
170
170
  _LOGGER.debug('Arm/disarm state change: %s', state)
171
- G90Callback.invoke(self._armdisarm_cb, state)
171
+ G90Callback.invoke(self.on_armdisarm, state)
172
172
  return
173
173
 
174
174
  if alert.type == G90AlertTypes.ALARM:
175
175
  _LOGGER.debug('Alarm: %s', alert.zone_name)
176
- G90Callback.invoke(self._alarm_cb, alert.event_id, alert.zone_name)
176
+ G90Callback.invoke(
177
+ self.on_alarm,
178
+ alert.event_id, alert.zone_name
179
+ )
177
180
  return
178
181
 
179
182
  _LOGGER.warning('Unknown alert received from %s:%s:'
180
183
  ' type %s, data %s',
181
184
  addr[0], addr[1], alert.type, alert)
182
185
 
186
+ # Implementation of datagram protocol,
187
+ # https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols
188
+ def connection_made(self, transport):
189
+ """
190
+ Invoked when connection from the device is made.
191
+ """
192
+
193
+ def connection_lost(self, exc):
194
+ """
195
+ Same but when the connection is lost.
196
+ """
197
+
183
198
  def datagram_received(self, data, addr): # pylint:disable=R0911
184
199
  """
185
- tbd
200
+ Invoked from datagram is received from the device.
186
201
  """
187
202
  s_data = data.decode('utf-8')
188
203
  if not s_data.endswith('\0'):
@@ -228,37 +243,34 @@ class G90DeviceNotificationProtocol:
228
243
  _LOGGER.warning('Unknown message received from %s:%s: %s',
229
244
  addr[0], addr[1], message)
230
245
 
246
+ async def on_armdisarm(self, state):
247
+ """
248
+ Invoked when device is armed or disarmed.
249
+ """
231
250
 
232
- class G90DeviceNotifications:
233
- """
234
- tbd
235
- """
236
- def __init__(self, port, host,
237
- armdisarm_cb=None, sensor_cb=None,
238
- door_open_close_cb=None, alarm_cb=None):
239
- # pylint: disable=too-many-arguments
240
- self._notification_transport = None
241
- self._host = host
242
- self._port = port
243
- self._armdisarm_cb = armdisarm_cb
244
- self._sensor_cb = sensor_cb
245
- self._door_open_close_cb = door_open_close_cb
246
- self._alarm_cb = alarm_cb
247
-
248
- def proto_factory(self):
251
+ async def on_sensor_activity(self, idx, name):
249
252
  """
250
- tbd
253
+ Invoked on sensor activity.
254
+ """
255
+
256
+ async def on_door_open_close(self, event_id, zone_name, is_open):
257
+ """
258
+ Invoked when door sensor reports it opened or closed.
259
+ """
260
+
261
+ async def on_low_battery(self, event_id, zone_name):
262
+ """
263
+ Invoked when a sensor reports it is low on battery.
264
+ """
265
+
266
+ async def on_alarm(self, event_id, zone_name):
267
+ """
268
+ Invoked when device triggers the alarm.
251
269
  """
252
- return G90DeviceNotificationProtocol(
253
- armdisarm_cb=self._armdisarm_cb,
254
- sensor_cb=self._sensor_cb,
255
- door_open_close_cb=self._door_open_close_cb,
256
- alarm_cb=self._alarm_cb
257
- )
258
270
 
259
271
  async def listen(self):
260
272
  """
261
- tbd
273
+ Listens for notifications/alers from the device.
262
274
  """
263
275
  try:
264
276
  loop = asyncio.get_running_loop()
@@ -266,15 +278,29 @@ class G90DeviceNotifications:
266
278
  loop = asyncio.get_event_loop()
267
279
 
268
280
  _LOGGER.debug('Creating UDP endpoint for %s:%s',
269
- self._host, self._port)
281
+ self._notifications_host,
282
+ self._notifications_port)
270
283
  (self._notification_transport,
271
284
  _protocol) = await loop.create_datagram_endpoint(
272
- self.proto_factory,
273
- local_addr=(self._host, self._port))
285
+ lambda: self,
286
+ local_addr=(
287
+ self._notifications_host, self._notifications_port
288
+ ))
289
+
290
+ @property
291
+ def listener_started(self):
292
+ """
293
+ Indicates if the listener of the device notifications has been started.
294
+
295
+ :rtype: bool
296
+ """
297
+ return self._notification_transport is not None
274
298
 
275
299
  def close(self):
276
300
  """
277
- tbd
301
+ Closes the listener.
278
302
  """
279
303
  if self._notification_transport:
304
+ _LOGGER.debug('No longer listening for device notifications')
280
305
  self._notification_transport.close()
306
+ self._notification_transport = None
pyg90alarm/discovery.py CHANGED
@@ -32,34 +32,28 @@ from .const import G90Commands
32
32
  _LOGGER = logging.getLogger(__name__)
33
33
 
34
34
 
35
- class G90DiscoveryProtocol:
35
+ class G90Discovery(G90BaseCommand):
36
36
  """
37
37
  tbd
38
-
39
- :meta private:
40
38
  """
41
- def __init__(self, parent):
42
- """
43
- tbd
44
- """
45
- self._parent = parent
46
-
47
- def connection_made(self, transport):
48
- """
49
- tbd
50
- """
51
-
52
- def connection_lost(self, exc):
39
+ # pylint: disable=too-few-public-methods
40
+ def __init__(self, timeout=10, **kwargs):
53
41
  """
54
42
  tbd
55
43
  """
44
+ # pylint: disable=too-many-arguments
45
+ super().__init__(code=G90Commands.GETHOSTINFO, timeout=timeout,
46
+ **kwargs)
47
+ self._discovered_devices = []
56
48
 
49
+ # Implementation of datagram protocol,
50
+ # https://docs.python.org/3/library/asyncio-protocol.html#datagram-protocols
57
51
  def datagram_received(self, data, addr):
58
52
  """
59
53
  tbd
60
54
  """
61
55
  try:
62
- ret = self._parent.from_wire(data)
56
+ ret = self.from_wire(data)
63
57
  host_info = G90HostInfo(*ret)
64
58
  _LOGGER.debug('Received from %s:%s: %s', addr[0], addr[1], ret)
65
59
  res = {
@@ -69,31 +63,11 @@ class G90DiscoveryProtocol:
69
63
  }
70
64
  res.update(host_info._asdict())
71
65
  _LOGGER.debug('Discovered device: %s', res)
72
- self._parent.add_device(res)
66
+ self.add_device(res)
73
67
 
74
68
  except Exception as exc: # pylint: disable=broad-except
75
69
  _LOGGER.warning('Got exception, ignoring: %s', exc)
76
70
 
77
- def error_received(self, exc):
78
- """
79
- tbd
80
- """
81
-
82
-
83
- class G90Discovery(G90BaseCommand):
84
- """
85
- tbd
86
- """
87
- # pylint: disable=too-few-public-methods
88
- def __init__(self, timeout=10, **kwargs):
89
- """
90
- tbd
91
- """
92
- # pylint: disable=too-many-arguments
93
- super().__init__(code=G90Commands.GETHOSTINFO, timeout=timeout,
94
- **kwargs)
95
- self._discovered_devices = []
96
-
97
71
  async def process(self):
98
72
  """
99
73
  tbd
@@ -118,9 +92,3 @@ class G90Discovery(G90BaseCommand):
118
92
  tbd
119
93
  """
120
94
  self._discovered_devices.append(value)
121
-
122
- def _proto_factory(self):
123
- """
124
- tbd
125
- """
126
- return G90DiscoveryProtocol(self)
@@ -172,6 +172,7 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
172
172
  self._subindex = subindex
173
173
  self._occupancy = False
174
174
  self._state_callback = None
175
+ self._low_battery_callback = None
175
176
  self._proto_idx = proto_idx
176
177
  self._extra_data = None
177
178
 
@@ -217,6 +218,25 @@ class G90Sensor: # pylint:disable=too-many-instance-attributes
217
218
  """
218
219
  self._state_callback = value
219
220
 
221
+ @property
222
+ def low_battery_callback(self):
223
+ """
224
+ Returns callback the sensor might have set for low battery condition.
225
+
226
+ :return: Sensor's low battery callback
227
+ :rtype: object
228
+ """
229
+ return self._low_battery_callback
230
+
231
+ @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
+ self._low_battery_callback = value
239
+
220
240
  @property
221
241
  def occupancy(self):
222
242
  """