pyg90alarm 1.10.0__tar.gz → 1.11.0__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.
Files changed (64) hide show
  1. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/.github/workflows/main.yml +3 -2
  2. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/PKG-INFO +7 -2
  3. pyg90alarm-1.11.0/docs/requirements.txt +10 -0
  4. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/pyproject.toml +5 -0
  5. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/alarm.py +9 -13
  6. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/base_cmd.py +13 -20
  7. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/const.py +1 -0
  8. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/definitions/sensors.py +241 -1
  9. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/device_notifications.py +11 -16
  10. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/targeted_discovery.py +1 -1
  11. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm.egg-info/PKG-INFO +7 -2
  12. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm.egg-info/SOURCES.txt +2 -2
  13. pyg90alarm-1.11.0/tests/conftest.py +66 -0
  14. pyg90alarm-1.11.0/tests/device_mock.py +264 -0
  15. pyg90alarm-1.11.0/tests/test_alarm.py +679 -0
  16. pyg90alarm-1.11.0/tests/test_base_commands.py +188 -0
  17. pyg90alarm-1.11.0/tests/test_callback.py +27 -0
  18. pyg90alarm-1.11.0/tests/test_discovery.py +87 -0
  19. pyg90alarm-1.11.0/tests/test_notifications.py +245 -0
  20. pyg90alarm-1.11.0/tests/test_paginated_commands.py +83 -0
  21. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/tox.ini +6 -5
  22. pyg90alarm-1.10.0/docs/requirements.txt +0 -4
  23. pyg90alarm-1.10.0/tests/__init__.py +0 -11
  24. pyg90alarm-1.10.0/tests/fixtures.py +0 -31
  25. pyg90alarm-1.10.0/tests/test_alarm.py +0 -669
  26. pyg90alarm-1.10.0/tests/test_base_commands.py +0 -131
  27. pyg90alarm-1.10.0/tests/test_callback.py +0 -29
  28. pyg90alarm-1.10.0/tests/test_discovery.py +0 -105
  29. pyg90alarm-1.10.0/tests/test_notifications.py +0 -238
  30. pyg90alarm-1.10.0/tests/test_paginated_commands.py +0 -75
  31. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/.github/CODEOWNERS +0 -0
  32. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/.gitignore +0 -0
  33. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/.pylintrc +0 -0
  34. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/.readthedocs.yaml +0 -0
  35. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/LICENSE +0 -0
  36. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/MANIFEST.in +0 -0
  37. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/README.rst +0 -0
  38. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/.DS_Store +0 -0
  39. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/.gitignore +0 -0
  40. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/api-docs.rst +0 -0
  41. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/conf.py +0 -0
  42. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/index.rst +0 -0
  43. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/docs/protocol.rst +0 -0
  44. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/setup.cfg +0 -0
  45. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/setup.py +0 -0
  46. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/sonar-project.properties +0 -0
  47. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/__init__.py +0 -0
  48. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/callback.py +0 -0
  49. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/config.py +0 -0
  50. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/definitions/__init__.py +0 -0
  51. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/discovery.py +0 -0
  52. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/entities/__init__.py +0 -0
  53. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/entities/device.py +0 -0
  54. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/entities/sensor.py +0 -0
  55. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/exceptions.py +0 -0
  56. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/history.py +0 -0
  57. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/host_info.py +0 -0
  58. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/host_status.py +0 -0
  59. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/paginated_cmd.py +0 -0
  60. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/paginated_result.py +0 -0
  61. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm/user_data_crc.py +0 -0
  62. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm.egg-info/dependency_links.txt +0 -0
  63. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm.egg-info/requires.txt +0 -0
  64. {pyg90alarm-1.10.0 → pyg90alarm-1.11.0}/src/pyg90alarm.egg-info/top_level.txt +0 -0
@@ -28,6 +28,9 @@ jobs:
28
28
  - os: ubuntu-latest
29
29
  python: '3.10'
30
30
  toxenv: py
31
+ - os: ubuntu-latest
32
+ python: '3.11'
33
+ toxenv: py
31
34
  runs-on: ${{ matrix.os }}
32
35
  steps:
33
36
  - name: Checkout the code
@@ -45,8 +48,6 @@ jobs:
45
48
  python -m pip install --upgrade setuptools pip tox virtualenv coverage
46
49
  - name: Run the tests
47
50
  run: tox -e ${{ matrix.toxenv }}
48
- - name: Combine Coverage reports
49
- run: coverage combine
50
51
  - name: Generage Coverage combined XML report
51
52
  run: coverage xml
52
53
  - name: Determine package version
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyg90alarm
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: G90 Alarm system protocol
5
5
  Home-page: https://github.com/hostcc/pyg90alarm
6
6
  Author: Ilia Sotnikov
@@ -22,10 +22,15 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3 :: Only
23
23
  Requires-Python: >=3.7, <4
24
24
  Description-Content-Type: text/x-rst
25
+ License-File: LICENSE
25
26
  Provides-Extra: dev
27
+ Requires-Dist: check-manifest; extra == "dev"
26
28
  Provides-Extra: test
29
+ Requires-Dist: coverage; extra == "test"
30
+ Requires-Dist: asynctest; extra == "test"
27
31
  Provides-Extra: docs
28
- License-File: LICENSE
32
+ Requires-Dist: sphinx; extra == "docs"
33
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
29
34
 
30
35
  .. image:: https://github.com/hostcc/pyg90alarm/actions/workflows/main.yml/badge.svg?branch=master
31
36
  :target: https://github.com/hostcc/pyg90alarm/tree/master
@@ -0,0 +1,10 @@
1
+ sphinx-rtd-theme
2
+ enum-tools[sphinx]
3
+ pygments>=2.15.0 # not directly required, pinned by Snyk to avoid a vulnerability
4
+ sphinx>=3.3.0 # not directly required, pinned by Snyk to avoid a vulnerability
5
+ setuptools>=65.5.1 # not directly required, pinned by Snyk to avoid a vulnerability
6
+ certifi>=2023.7.22 # not directly required, pinned by Snyk to avoid a vulnerability
7
+ requests>=2.32.0 # not directly required, pinned by Snyk to avoid a vulnerability
8
+ jinja2>=3.1.4 # not directly required, pinned by Snyk to avoid a vulnerability
9
+ requests>=2.31.0 # not directly required, pinned by Snyk to avoid a vulnerability
10
+ idna>=3.7 # not directly required, pinned by Snyk to avoid a vulnerability
@@ -6,3 +6,8 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [tool.setuptools_scm]
8
8
  local_scheme = "no-local-version"
9
+
10
+ [tool.pytest.ini_options]
11
+ log_cli = 1
12
+ log_cli_level = "error"
13
+ asyncio_mode = "auto"
@@ -54,6 +54,7 @@ from .const import (
54
54
  G90Commands, REMOTE_PORT,
55
55
  REMOTE_TARGETED_DISCOVERY_PORT,
56
56
  LOCAL_TARGETED_DISCOVERY_PORT,
57
+ NOTIFICATIONS_PORT,
57
58
  G90ArmDisarmTypes,
58
59
  )
59
60
  from .base_cmd import G90BaseCommand
@@ -87,23 +88,18 @@ class G90Alarm: # pylint: disable=too-many-public-methods
87
88
  protocol commands on WiFi interface, currently the devices don't allow it
88
89
  to be customized
89
90
  :type port: int, optional
90
- :param sock: The existing socket to operate on, instead of
91
- creating one internally. Primarily used by the tests to mock the network
92
- traffic
93
- :type sock: socket.socket or None, optional
94
91
  :param reset_occupancy_interval: The interval upon that the sensors are
95
92
  simulated to go into inactive state.
96
93
  :type reset_occupancy_interval: int, optional
97
94
  """
98
95
  # pylint: disable=too-many-instance-attributes
99
- def __init__(self, host, port=REMOTE_PORT, sock=None,
96
+ def __init__(self, host, port=REMOTE_PORT,
100
97
  reset_occupancy_interval=3):
101
98
  self._host = host
102
99
  self._port = port
103
100
  self._sensors = []
104
101
  self._devices = []
105
102
  self._notifications = None
106
- self._sock = sock
107
103
  self._sensor_cb = None
108
104
  self._armdisarm_cb = None
109
105
  self._door_open_close_cb = None
@@ -124,7 +120,7 @@ class G90Alarm: # pylint: disable=too-many-public-methods
124
120
  command invocation
125
121
  """
126
122
  cmd = await G90BaseCommand(
127
- self._host, self._port, code, data, sock=self._sock).process()
123
+ self._host, self._port, code, data).process()
128
124
  return cmd.result
129
125
 
130
126
  def paginated_result(self, code, start=1, end=None):
@@ -141,7 +137,7 @@ class G90Alarm: # pylint: disable=too-many-public-methods
141
137
  yields :class:`.G90PaginatedResponse` instance
142
138
  """
143
139
  return G90PaginatedResult(
144
- self._host, self._port, code, start, end, sock=self._sock
140
+ self._host, self._port, code, start, end
145
141
  ).process()
146
142
 
147
143
  @classmethod
@@ -585,19 +581,19 @@ class G90Alarm: # pylint: disable=too-many-public-methods
585
581
  def alarm_callback(self, value):
586
582
  self._alarm_cb = value
587
583
 
588
- async def listen_device_notifications(self, sock=None):
584
+ async def listen_device_notifications(
585
+ self, host='0.0.0.0', port=NOTIFICATIONS_PORT
586
+ ):
589
587
  """
590
588
  Starts internal listener for device notifications/alerts.
591
589
 
592
- :param sock: socket instance to listen on, mostly used by tests
593
- :type: socket.socket
594
590
  """
595
591
  self._notifications = G90DeviceNotifications(
592
+ host=host, port=port,
596
593
  sensor_cb=self._internal_sensor_cb,
597
594
  door_open_close_cb=self._internal_door_open_close_cb,
598
595
  armdisarm_cb=self._internal_armdisarm_cb,
599
- alarm_cb=self._internal_alarm_cb,
600
- sock=sock)
596
+ alarm_cb=self._internal_alarm_cb)
601
597
  await self._notifications.listen()
602
598
 
603
599
  def close_device_notifications(self):
@@ -110,7 +110,7 @@ class G90BaseCommand:
110
110
 
111
111
  def __init__(self, host, port, code,
112
112
  data=None, local_port=None,
113
- timeout=3.0, retries=3, sock=None):
113
+ timeout=3.0, retries=3):
114
114
  """
115
115
  tbd
116
116
  """
@@ -130,9 +130,8 @@ class G90BaseCommand:
130
130
  # No whitespace around entities
131
131
  separators=(',', ':'))
132
132
  self._resp = G90Header()
133
- self._sock = sock
134
133
 
135
- def _proto_factory(self): # pylint: disable=no-self-use
134
+ def _proto_factory(self):
136
135
  """
137
136
  tbd
138
137
  """
@@ -147,23 +146,17 @@ class G90BaseCommand:
147
146
  except AttributeError:
148
147
  loop = asyncio.get_event_loop()
149
148
 
150
- if self._sock:
151
- _LOGGER.debug('Using provided socket %s', self._sock)
152
- transport, protocol = await loop.create_datagram_endpoint(
153
- self._proto_factory,
154
- sock=self._sock)
155
- else:
156
- _LOGGER.debug('Creating UDP endpoint for %s:%s',
157
- self.host, self.port)
158
- extra_kwargs = {}
159
- if self._local_port:
160
- extra_kwargs['local_addr'] = ('0.0.0.0', self._local_port)
161
-
162
- transport, protocol = await loop.create_datagram_endpoint(
163
- self._proto_factory,
164
- remote_addr=(self.host, self.port),
165
- **extra_kwargs,
166
- allow_broadcast=True)
149
+ _LOGGER.debug('Creating UDP endpoint for %s:%s',
150
+ self.host, self.port)
151
+ extra_kwargs = {}
152
+ if self._local_port:
153
+ extra_kwargs['local_addr'] = ('0.0.0.0', self._local_port)
154
+
155
+ transport, protocol = await loop.create_datagram_endpoint(
156
+ self._proto_factory,
157
+ remote_addr=(self.host, self.port),
158
+ **extra_kwargs,
159
+ allow_broadcast=True)
167
160
 
168
161
  return transport, protocol
169
162
 
@@ -27,6 +27,7 @@ from enum import IntEnum
27
27
  REMOTE_PORT = 12368
28
28
  REMOTE_TARGETED_DISCOVERY_PORT = 12900
29
29
  LOCAL_TARGETED_DISCOVERY_PORT = 12901
30
+ NOTIFICATIONS_PORT = 12901
30
31
 
31
32
  CMD_PAGE_SIZE = 10
32
33
 
@@ -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,
@@ -36,6 +36,7 @@ from .const import (
36
36
  G90AlertSources,
37
37
  )
38
38
 
39
+
39
40
  _LOGGER = logging.getLogger(__name__)
40
41
 
41
42
 
@@ -232,16 +233,17 @@ class G90DeviceNotifications:
232
233
  """
233
234
  tbd
234
235
  """
235
- def __init__(self, port=12901, armdisarm_cb=None, sensor_cb=None,
236
- door_open_close_cb=None, alarm_cb=None, sock=None):
236
+ def __init__(self, port, host,
237
+ armdisarm_cb=None, sensor_cb=None,
238
+ door_open_close_cb=None, alarm_cb=None):
237
239
  # pylint: disable=too-many-arguments
238
240
  self._notification_transport = None
241
+ self._host = host
239
242
  self._port = port
240
243
  self._armdisarm_cb = armdisarm_cb
241
244
  self._sensor_cb = sensor_cb
242
245
  self._door_open_close_cb = door_open_close_cb
243
246
  self._alarm_cb = alarm_cb
244
- self._sock = sock
245
247
 
246
248
  def proto_factory(self):
247
249
  """
@@ -263,19 +265,12 @@ class G90DeviceNotifications:
263
265
  except AttributeError:
264
266
  loop = asyncio.get_event_loop()
265
267
 
266
- if self._sock:
267
- _LOGGER.debug('Using provided socket %s', self._sock)
268
- (self._notification_transport,
269
- _protocol) = await loop.create_datagram_endpoint(
270
- self.proto_factory,
271
- sock=self._sock)
272
- else:
273
- _LOGGER.debug('Creating UDP endpoint for 0.0.0.0:%s',
274
- self._port)
275
- (self._notification_transport,
276
- _protocol) = await loop.create_datagram_endpoint(
277
- self.proto_factory,
278
- local_addr=('0.0.0.0', self._port))
268
+ _LOGGER.debug('Creating UDP endpoint for %s:%s',
269
+ self._host, self._port)
270
+ (self._notification_transport,
271
+ _protocol) = await loop.create_datagram_endpoint(
272
+ self.proto_factory,
273
+ local_addr=(self._host, self._port))
279
274
 
280
275
  def close(self):
281
276
  """
@@ -119,7 +119,7 @@ class G90TargetedDiscovery(G90Discovery):
119
119
  super().__init__(**kwargs)
120
120
  self._device_id = device_id
121
121
 
122
- def to_wire(self): # pylint: disable=no-self-use
122
+ def to_wire(self):
123
123
  """
124
124
  tbd
125
125
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyg90alarm
3
- Version: 1.10.0
3
+ Version: 1.11.0
4
4
  Summary: G90 Alarm system protocol
5
5
  Home-page: https://github.com/hostcc/pyg90alarm
6
6
  Author: Ilia Sotnikov
@@ -22,10 +22,15 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3 :: Only
23
23
  Requires-Python: >=3.7, <4
24
24
  Description-Content-Type: text/x-rst
25
+ License-File: LICENSE
25
26
  Provides-Extra: dev
27
+ Requires-Dist: check-manifest; extra == "dev"
26
28
  Provides-Extra: test
29
+ Requires-Dist: coverage; extra == "test"
30
+ Requires-Dist: asynctest; extra == "test"
27
31
  Provides-Extra: docs
28
- License-File: LICENSE
32
+ Requires-Dist: sphinx; extra == "docs"
33
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
29
34
 
30
35
  .. image:: https://github.com/hostcc/pyg90alarm/actions/workflows/main.yml/badge.svg?branch=master
31
36
  :target: https://github.com/hostcc/pyg90alarm/tree/master
@@ -44,8 +44,8 @@ src/pyg90alarm/definitions/sensors.py
44
44
  src/pyg90alarm/entities/__init__.py
45
45
  src/pyg90alarm/entities/device.py
46
46
  src/pyg90alarm/entities/sensor.py
47
- tests/__init__.py
48
- tests/fixtures.py
47
+ tests/conftest.py
48
+ tests/device_mock.py
49
49
  tests/test_alarm.py
50
50
  tests/test_base_commands.py
51
51
  tests/test_callback.py
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2023 Ilia Sotnikov
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ # SOFTWARE.
20
+
21
+ """
22
+ Performs runtime configuration and exposes custom fixtures for Pytest.
23
+ """
24
+ import pytest
25
+ from device_mock import DeviceMock
26
+
27
+
28
+ def pytest_configure(config):
29
+ """
30
+ Configures `pytest`.
31
+ """
32
+ config.addinivalue_line("markers", "g90device")
33
+
34
+
35
+ @pytest.fixture
36
+ async def mock_device(request, unused_udp_port_factory):
37
+ """
38
+ Fixture to instantiate a simulated G90 device allocating random unused
39
+ ports for network exchanges.
40
+
41
+ The fixture should be customized with `g90device` mark containing
42
+ `sent_data` and `notification_data` lists of bytes to simulate the messages
43
+ the device sends back to client and notification messages sent to client,
44
+ respectively.
45
+ """
46
+ marker = getattr(
47
+ request.node
48
+ .get_closest_marker('g90device'),
49
+ 'kwargs', {}
50
+ )
51
+ data = marker.get('sent_data', [])
52
+ notification_data = marker.get('notification_data', [])
53
+
54
+ # Allocate unused ports to listen for client requests on, and to send
55
+ # notification messages to, respectively
56
+
57
+ # Note the `unised_udp_port_factory` comes from `pytest-asyncio` package
58
+ device_port = unused_udp_port_factory()
59
+ notification_port = unused_udp_port_factory()
60
+ device = DeviceMock(
61
+ data, notification_data,
62
+ device_port=device_port, notification_port=notification_port
63
+ )
64
+ await device.start()
65
+ yield device
66
+ device.stop()