pyg90alarm 1.10.0__tar.gz → 2.3.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 (99) hide show
  1. pyg90alarm-2.3.0/.github/dependabot.yml +26 -0
  2. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/.github/workflows/main.yml +17 -14
  3. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/.gitignore +3 -0
  4. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/MANIFEST.in +4 -0
  5. pyg90alarm-2.3.0/PKG-INFO +277 -0
  6. pyg90alarm-2.3.0/README.rst +230 -0
  7. pyg90alarm-2.3.0/docs/cloud-protocol.rst +85 -0
  8. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/docs/conf.py +1 -0
  9. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/docs/index.rst +2 -1
  10. pyg90alarm-1.10.0/docs/protocol.rst → pyg90alarm-2.3.0/docs/local-protocol.rst +10 -12
  11. pyg90alarm-2.3.0/docs/requirements.txt +11 -0
  12. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/pyproject.toml +9 -0
  13. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/setup.py +4 -3
  14. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/sonar-project.properties +2 -1
  15. pyg90alarm-2.3.0/src/pyg90alarm/__init__.py +84 -0
  16. pyg90alarm-2.3.0/src/pyg90alarm/alarm.py +1274 -0
  17. pyg90alarm-2.3.0/src/pyg90alarm/callback.py +146 -0
  18. pyg90alarm-1.10.0/src/pyg90alarm/host_status.py → pyg90alarm-2.3.0/src/pyg90alarm/cloud/__init__.py +8 -16
  19. pyg90alarm-1.10.0/src/pyg90alarm/config.py → pyg90alarm-2.3.0/src/pyg90alarm/cloud/const.py +32 -31
  20. pyg90alarm-2.3.0/src/pyg90alarm/cloud/messages.py +593 -0
  21. pyg90alarm-2.3.0/src/pyg90alarm/cloud/notifications.py +410 -0
  22. pyg90alarm-2.3.0/src/pyg90alarm/cloud/protocol.py +518 -0
  23. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm/const.py +73 -2
  24. pyg90alarm-2.3.0/src/pyg90alarm/definitions/base.py +247 -0
  25. pyg90alarm-2.3.0/src/pyg90alarm/definitions/devices.py +366 -0
  26. pyg90alarm-2.3.0/src/pyg90alarm/definitions/sensors.py +843 -0
  27. pyg90alarm-2.3.0/src/pyg90alarm/entities/base_entity.py +93 -0
  28. pyg90alarm-2.3.0/src/pyg90alarm/entities/base_list.py +268 -0
  29. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm/entities/device.py +34 -13
  30. pyg90alarm-2.3.0/src/pyg90alarm/entities/device_list.py +156 -0
  31. pyg90alarm-2.3.0/src/pyg90alarm/entities/sensor.py +891 -0
  32. pyg90alarm-2.3.0/src/pyg90alarm/entities/sensor_list.py +183 -0
  33. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm/exceptions.py +24 -0
  34. pyg90alarm-2.3.0/src/pyg90alarm/local/__init__.py +0 -0
  35. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/base_cmd.py +140 -142
  36. pyg90alarm-2.3.0/src/pyg90alarm/local/config.py +157 -0
  37. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/discovery.py +39 -62
  38. pyg90alarm-2.3.0/src/pyg90alarm/local/history.py +272 -0
  39. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/host_info.py +27 -25
  40. pyg90alarm-1.10.0/src/pyg90alarm/history.py → pyg90alarm-2.3.0/src/pyg90alarm/local/host_status.py +19 -23
  41. pyg90alarm-2.3.0/src/pyg90alarm/local/notifications.py +117 -0
  42. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/paginated_cmd.py +48 -35
  43. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/paginated_result.py +15 -9
  44. pyg90alarm-2.3.0/src/pyg90alarm/local/targeted_discovery.py +162 -0
  45. {pyg90alarm-1.10.0/src/pyg90alarm → pyg90alarm-2.3.0/src/pyg90alarm/local}/user_data_crc.py +18 -13
  46. pyg90alarm-2.3.0/src/pyg90alarm/notifications/__init__.py +0 -0
  47. pyg90alarm-2.3.0/src/pyg90alarm/notifications/base.py +481 -0
  48. pyg90alarm-2.3.0/src/pyg90alarm/notifications/protocol.py +127 -0
  49. pyg90alarm-2.3.0/src/pyg90alarm/py.typed +0 -0
  50. pyg90alarm-2.3.0/src/pyg90alarm.egg-info/PKG-INFO +277 -0
  51. pyg90alarm-2.3.0/src/pyg90alarm.egg-info/SOURCES.txt +77 -0
  52. pyg90alarm-2.3.0/tests/__init__.py +0 -0
  53. pyg90alarm-2.3.0/tests/conftest.py +126 -0
  54. pyg90alarm-2.3.0/tests/device_mock.py +706 -0
  55. pyg90alarm-2.3.0/tests/test_alarm.py +781 -0
  56. pyg90alarm-2.3.0/tests/test_base_commands.py +323 -0
  57. pyg90alarm-2.3.0/tests/test_cloud_notifications.py +502 -0
  58. pyg90alarm-2.3.0/tests/test_config.py +53 -0
  59. pyg90alarm-2.3.0/tests/test_devices.py +328 -0
  60. pyg90alarm-2.3.0/tests/test_discovery.py +132 -0
  61. pyg90alarm-2.3.0/tests/test_history.py +233 -0
  62. pyg90alarm-2.3.0/tests/test_local_notifications.py +677 -0
  63. pyg90alarm-2.3.0/tests/test_paginated_commands.py +111 -0
  64. pyg90alarm-2.3.0/tests/test_sensor.py +571 -0
  65. pyg90alarm-2.3.0/tox.ini +32 -0
  66. pyg90alarm-1.10.0/PKG-INFO +0 -168
  67. pyg90alarm-1.10.0/README.rst +0 -139
  68. pyg90alarm-1.10.0/docs/requirements.txt +0 -4
  69. pyg90alarm-1.10.0/src/pyg90alarm/__init__.py +0 -30
  70. pyg90alarm-1.10.0/src/pyg90alarm/alarm.py +0 -644
  71. pyg90alarm-1.10.0/src/pyg90alarm/callback.py +0 -118
  72. pyg90alarm-1.10.0/src/pyg90alarm/definitions/sensors.py +0 -618
  73. pyg90alarm-1.10.0/src/pyg90alarm/device_notifications.py +0 -285
  74. pyg90alarm-1.10.0/src/pyg90alarm/entities/sensor.py +0 -465
  75. pyg90alarm-1.10.0/src/pyg90alarm/targeted_discovery.py +0 -133
  76. pyg90alarm-1.10.0/src/pyg90alarm.egg-info/PKG-INFO +0 -168
  77. pyg90alarm-1.10.0/src/pyg90alarm.egg-info/SOURCES.txt +0 -54
  78. pyg90alarm-1.10.0/tests/__init__.py +0 -11
  79. pyg90alarm-1.10.0/tests/fixtures.py +0 -31
  80. pyg90alarm-1.10.0/tests/test_alarm.py +0 -669
  81. pyg90alarm-1.10.0/tests/test_base_commands.py +0 -131
  82. pyg90alarm-1.10.0/tests/test_callback.py +0 -29
  83. pyg90alarm-1.10.0/tests/test_discovery.py +0 -105
  84. pyg90alarm-1.10.0/tests/test_notifications.py +0 -238
  85. pyg90alarm-1.10.0/tests/test_paginated_commands.py +0 -75
  86. pyg90alarm-1.10.0/tox.ini +0 -46
  87. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/.github/CODEOWNERS +0 -0
  88. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/.pylintrc +0 -0
  89. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/.readthedocs.yaml +0 -0
  90. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/LICENSE +0 -0
  91. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/docs/.DS_Store +0 -0
  92. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/docs/.gitignore +0 -0
  93. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/docs/api-docs.rst +0 -0
  94. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/setup.cfg +0 -0
  95. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm/definitions/__init__.py +0 -0
  96. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm/entities/__init__.py +0 -0
  97. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm.egg-info/dependency_links.txt +0 -0
  98. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm.egg-info/requires.txt +0 -0
  99. {pyg90alarm-1.10.0 → pyg90alarm-2.3.0}/src/pyg90alarm.egg-info/top_level.txt +0 -0
@@ -0,0 +1,26 @@
1
+ ---
2
+ version: 2
3
+ updates:
4
+ - package-ecosystem: "pip"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "daily"
8
+ commit-message:
9
+ prefix: "deps"
10
+ include: "scope"
11
+ labels:
12
+ - "dependencies"
13
+ - "automated"
14
+ open-pull-requests-limit: 5
15
+
16
+ - package-ecosystem: "github-actions"
17
+ directory: "/"
18
+ schedule:
19
+ interval: "daily"
20
+ commit-message:
21
+ prefix: "deps"
22
+ include: "scope"
23
+ labels:
24
+ - "dependencies"
25
+ - "automated"
26
+ open-pull-requests-limit: 5
@@ -17,27 +17,30 @@ jobs:
17
17
  matrix:
18
18
  include:
19
19
  - os: ubuntu-latest
20
- python: '3.7'
20
+ python: '3.9'
21
21
  toxenv: py
22
22
  - os: ubuntu-latest
23
- python: '3.8'
23
+ python: '3.10'
24
24
  toxenv: py
25
25
  - os: ubuntu-latest
26
- python: '3.9'
26
+ python: '3.11'
27
27
  toxenv: py
28
28
  - os: ubuntu-latest
29
- python: '3.10'
29
+ python: '3.12'
30
+ toxenv: py
31
+ - os: ubuntu-latest
32
+ python: '3.13'
30
33
  toxenv: py
31
34
  runs-on: ${{ matrix.os }}
32
35
  steps:
33
36
  - name: Checkout the code
34
- uses: actions/checkout@v3
37
+ uses: actions/checkout@v6
35
38
  with:
36
39
  # Disable shallow clone for Sonar scanner, as it needs access to the
37
40
  # history
38
41
  fetch-depth: 0
39
42
  - name: Set Python up
40
- uses: actions/setup-python@v4
43
+ uses: actions/setup-python@v6
41
44
  with:
42
45
  python-version: ${{ matrix.python }}
43
46
  - name: Install testing tools
@@ -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
@@ -55,7 +56,7 @@ jobs:
55
56
  package_version=`python3 setup.py --version`
56
57
  echo "VALUE=$package_version" >> $GITHUB_OUTPUT
57
58
  - name: SonarCloud scanning
58
- uses: sonarsource/sonarcloud-github-action@master
59
+ uses: SonarSource/sonarqube-scan-action@v6
59
60
  env:
60
61
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61
62
  SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -71,13 +72,15 @@ jobs:
71
72
  name: Publish to PyPi
72
73
  runs-on: ubuntu-latest
73
74
  needs: [tests]
75
+ permissions:
76
+ id-token: write # Required for trusted publishing
74
77
  steps:
75
78
  - name: Checkout the code
76
- uses: actions/checkout@v3
79
+ uses: actions/checkout@v6
77
80
  with:
78
81
  fetch-depth: 0 # `setuptools_scm` needs tags
79
82
  - name: Set Python up
80
- uses: actions/setup-python@v4
83
+ uses: actions/setup-python@v6
81
84
  with:
82
85
  python-version: 3.9
83
86
  - name: Install the PEP517 package builder
@@ -90,8 +93,8 @@ jobs:
90
93
  if: github.event_name != 'release'
91
94
  uses: pypa/gh-action-pypi-publish@release/v1
92
95
  with:
93
- password: ${{ secrets.TEST_PYPI_TOKEN }}
94
- repository_url: https://test.pypi.org/legacy/
96
+ repository-url: https://test.pypi.org/legacy/
97
+ attestations: true
95
98
  - name: Publish the release to PyPi
96
99
  # Publish to production PyPi only happens when a release published out
97
100
  # of the main branch
@@ -102,4 +105,4 @@ jobs:
102
105
  || github.event.release.target_commitish == 'master')
103
106
  uses: pypa/gh-action-pypi-publish@release/v1
104
107
  with:
105
- password: ${{ secrets.PYPI_TOKEN }}
108
+ attestations: true
@@ -4,6 +4,7 @@ dist/
4
4
  *.egg
5
5
  *.py[cod]
6
6
  __pycache__/
7
+ .venv/
7
8
  *.so
8
9
  *~
9
10
  .tox
@@ -14,3 +15,5 @@ flake8.txt
14
15
  pylint.txt
15
16
  .coverage*
16
17
  coverage.xml
18
+ .mypy_cache/
19
+ mypy/
@@ -8,3 +8,7 @@ include LICENSE
8
8
 
9
9
  # Include setup.py
10
10
  include setup.py
11
+
12
+ recursive-include src *.py py.typed
13
+
14
+ exclude requirements_dev.txt
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyg90alarm
3
+ Version: 2.3.0
4
+ Summary: G90 Alarm system protocol
5
+ Home-page: https://github.com/hostcc/pyg90alarm
6
+ Author: Ilia Sotnikov
7
+ Author-email: hostcc@gmail.com
8
+ Project-URL: Bug Reports, https://github.com/hostcc/pyg90alarm/issues
9
+ Project-URL: Source, https://github.com/hostcc/pyg90alarm/
10
+ Keywords: g90,alarm,protocol
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: Topic :: Home Automation
15
+ Classifier: Topic :: System :: Hardware
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3 :: Only
24
+ Requires-Python: >=3.9, <4
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE
27
+ Provides-Extra: dev
28
+ Requires-Dist: check-manifest; extra == "dev"
29
+ Provides-Extra: test
30
+ Requires-Dist: coverage; extra == "test"
31
+ Requires-Dist: asynctest; extra == "test"
32
+ Provides-Extra: docs
33
+ Requires-Dist: sphinx; extra == "docs"
34
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
35
+ Dynamic: author
36
+ Dynamic: author-email
37
+ Dynamic: classifier
38
+ Dynamic: description
39
+ Dynamic: description-content-type
40
+ Dynamic: home-page
41
+ Dynamic: keywords
42
+ Dynamic: license-file
43
+ Dynamic: project-url
44
+ Dynamic: provides-extra
45
+ Dynamic: requires-python
46
+ Dynamic: summary
47
+
48
+ .. image:: https://github.com/hostcc/pyg90alarm/actions/workflows/main.yml/badge.svg?branch=master
49
+ :target: https://github.com/hostcc/pyg90alarm/tree/master
50
+ :alt: Github workflow status
51
+ .. image:: https://readthedocs.org/projects/pyg90alarm/badge/?version=stable
52
+ :target: https://pyg90alarm.readthedocs.io/en/stable
53
+ :alt: ReadTheDocs status
54
+ .. image:: https://img.shields.io/github/v/release/hostcc/pyg90alarm
55
+ :target: https://github.com/hostcc/pyg90alarm/releases/latest
56
+ :alt: Latest GitHub release
57
+ .. image:: https://img.shields.io/pypi/v/pyg90alarm
58
+ :target: https://pypi.org/project/pyg90alarm/
59
+ :alt: Latest PyPI version
60
+
61
+ Description
62
+ ===========
63
+
64
+ Python package to control G90-based alarm systems.
65
+
66
+ Many manufacturers sell such systems under different brands - Golden Security,
67
+ PST, Kerui and others. Those are cheap low-end systems, typically equipped with
68
+ WiFi and possible GSM interfaces for connectivity, and support different range
69
+ of peripherals:
70
+
71
+ * Wired and wireless sensors
72
+ * Relays (switches)
73
+
74
+ ... and probably others
75
+
76
+ The package implements asynchronous I/O over most of code paths using
77
+ `asyncio <https://docs.python.org/3/library/asyncio.html>`_.
78
+
79
+ Disclaimer
80
+ ==========
81
+
82
+ The author has no affiliation or any relationship to any of the hardware
83
+ vendors in question. The code has been created upon many trial and error
84
+ iterations.
85
+
86
+ Motivation
87
+ ==========
88
+
89
+ The primary motivation creating the code is the comfort of using the security
90
+ system - the mobile applications provided by the vendor, called "Carener", is
91
+ slow and crashes sometimes. Instead, it would be awesome to have the system
92
+ integrated into larger ecosystems, like Home Assistant, HomeKit and such.
93
+ Hence, the code has been created to interact with the security system using
94
+ Python, and it opens up a way for further integrations.
95
+
96
+ Supported hardware
97
+ ==================
98
+
99
+ It might not be possible to list every system supported by the package due to
100
+ manufacturers naming the products differently. Here is the list of hardware
101
+ known to work with the package:
102
+
103
+ * `PST G90B Plus <http://www.cameralarms.com/products/auto_dial_alarm_system/185.html>`_
104
+
105
+ And the list of sensors, actual set of device should be notable larger as many
106
+ of other manufacturers produce similar items. The names in parenthesis are
107
+ taken from the alarm system documentation, for example, `Home Alarm GB90-Plus <https://archive.org/details/HomeAlarmGB90-Plus/G90B%20plus%20WIFIGSMGPRS%20alarm%20system%20user%20manual/page/n7/mode/2up>`_.
108
+
109
+ * Wired PIR sensors
110
+ * Wireless PIR sensors (WPD01, WMS08)
111
+ * Door/window sensors (WDS07, WRDS01)
112
+ * Water leak sensors (LSTC01)
113
+ * Smoke sensors (WSD02)
114
+ * Gas sensors (WGD01)
115
+ * Switches/relays (JDQ)
116
+
117
+ Basically, the alarm system uses 433 MHz communications for the wireless
118
+ devices using EV1527, PT2262 protocols. The mobile application also mentions
119
+ some devices using 2.4GHz, although details of the protocols haven't been
120
+ identified as no such hardware has been available for experimentation.
121
+
122
+ Known caveats
123
+ =============
124
+
125
+ * Wireless shutter sensor (WRDS01) doesn't send anything on sensor closed, only
126
+ when opened. In contrast, WDS07 wireless door sensor does both.
127
+ * Wireless relays (at least JDQ) use same RF code for switching on and off,
128
+ when configured in toggle mode. That means a RF signal repeater will make
129
+ controlling such relays unpredictable, since the code will be sent more than
130
+ once.
131
+ * Low battery notifications for wireless sensors (at least for WDS07 and WSD02)
132
+ are often missing, either due to the sensors not sending them or the device
133
+ doesn't receive those.
134
+ * Wired sensors toggle on line state change, i.e. those aren't limited to have
135
+ normal closed (NC) or normal open (NO) contacts only. Best used with NC
136
+ contact sensors though, since an intruder cutting the line will trigger the
137
+ alarm.
138
+
139
+ Device notifications
140
+ ====================
141
+
142
+ Local notifications
143
+ -------------------
144
+
145
+ There is a hidden device capability to send protocol notifications over the
146
+ WiFi interface, thus called local. The notifications are done using broadcast UDP packets with source/destination ports being ``45000:12901`` (non-configurable), and sent when the device has IP address of its WiFi interface set to ``10.10.10.250``. That is the same IP the device will allocate to the WiFi interface when AP (access point is enabled). Please note enabling the AP *is not* required for the notifications to be sent, only the IP address matters. Likely the firmware does a check internally and enables those when corresponding IP address is found on the WiFi interface.
147
+
148
+ Depending on your network setup, ensuring the `10.10.10.250` IP address is
149
+ allocated to the WiFi interface of the device might be as simple as DHCP
150
+ reservation. Please check the documentation of your networking gear on how to
151
+ set the IP address allocation up.
152
+
153
+ .. note:: Since the IP address trick above isn't something the device exposes
154
+ to the user, the functionality might change or even cease functioning upon a
155
+ firmware upgrade!
156
+
157
+ .. note:: The device notifications in question are fully local with no
158
+ dependency on the cloud or Internet connection on the device.
159
+
160
+ .. note:: If IP address trick doesn't work for you by a reason, the package
161
+ will still be able to perform the key functions - for example, arming or
162
+ disarming the panel, or reading the list of sensors. However, the sensor
163
+ status will not be reflected and those will always be reported as inactive,
164
+ since there is no way to read their state in a polled manner.
165
+
166
+ To work that limitation around the package now supports simulating device
167
+ notifications from periodically polling the history it records - the
168
+ simulation works only for the alerts, not notifications (e.g. notifications
169
+ include low battery events and alike). This also requires the particular
170
+ alert to be enabled in the mobile application, otherwise it won't be
171
+ recorded in the history.
172
+
173
+ For the local notifications to be enabled the ``G90Alarm.use_local_notifications()`` needs to be called upon constructing an instance of ``G90Alarm`` class, then ``G90Alarm.listen_notifications()`` to start processing those coming from the panel - the code fragment below demonstrates that though being incomplete since callbacks (e.g. ``G90Alarm.on_armdisarm()``) should be set for the actual processing of the notifications.
174
+
175
+ .. code:: python
176
+
177
+ from pyg90alarm import G90Alarm
178
+
179
+ # Create an instance of the alarm panel
180
+ alarm = G90Alarm(host='10.10.10.250')
181
+ # Enable local notifications
182
+ await alarm.use_local_notifications()
183
+ # Start listening for notifications
184
+ await alarm.listen_notifications()
185
+
186
+ Cloud notifications
187
+ -------------------
188
+
189
+ The cloud protocol is native to the panel and is used to interact with mobile application. The package can mimic the cloud server and interpret the messages the panel sends to the cloud, allowing to receive the notifications and alerts.
190
+ While the protocol also allows to send commands to the panel, it is not implemented and local protocol is used for that - i.e. when cloud notifications are in use the local protocol still utilized for sending commands to the panel.
191
+
192
+ The cloud protocol is TCP based and typically interacts with cloud service at known IP address and port (not customizable at panel side). To process the cloud notifications all the traffic from panel towards the cloud (IP address ``47.88.7.61`` and TCP port ``5678`` as of writing) needs to be diverted to the node where the package is running - depending on your network equipment it could be port forwarding, DNAT or other means. It is unclear whether the panel utilizes DNS to resolve the cloud service IP address, hence the documentation only mentions IP-based traffic redirection.
193
+
194
+ Please see
195
+ `the section <docs/cloud-protocol.rst>`_ for further details on the protocol.
196
+
197
+ The benefit of the cloud notifications is that the panel no longer required to have ``10.10.10.250`` IP address.
198
+
199
+ The package could act as:
200
+
201
+ - Standalone cloud server with no Internet connectivity or cloud service
202
+ required at all - good if you'd like to avoid having a vendor service involved. Please note the mobile application will show panel as offline in this mode
203
+ - Chained cloud server, where in addition to interpreting the notifications it
204
+ will also forward all packets received from the panel to the cloud server, and pass its responses back to the panel. This allows to have notifications processed by the package and the mobile application working as well.
205
+
206
+ .. note:: Sending packets upstream to the known IP address and port of the cloud server might result in those looped back (since traffic from panel to cloud service has to be redirected to the host where package runs), if your network equipment can't account for source address in redirection rules (i.e. limiting the port redirection to the panel's IP address). In that case you'll need another redirection, from the host where the package runs to the cloud service using an IP from your network. That way those two redirection rules will coexist correctly. To illustrate:
207
+
208
+ Port forwarding rule 1:
209
+
210
+ - Source: panel IP address
211
+ - Destination: 47.88.7.61
212
+ - Port: 5678
213
+ - Redirect to host: host where package runs
214
+ - Redirect to port: 5678 (or other port if you want to use it)
215
+
216
+
217
+ Port forwarding rule 2 (optional):
218
+
219
+ - Source: host where package runs
220
+ - Destination: an IP address from your network
221
+ - Port: 5678 (or other port if you want to use it)
222
+ - Redirect to : 47.88.7.61
223
+ - Redirect to port: 5678
224
+
225
+ The code fragments below demonstrate how to utilize both modes - please note those are incomplete, since no callbacks are set to process the notifications.
226
+
227
+ **Standalone mode**
228
+
229
+ .. code:: python
230
+
231
+ from pyg90alarm import G90Alarm
232
+
233
+ # Create an instance of the alarm panel
234
+ alarm = G90Alarm(host='<panel IP address>')
235
+ # Enable cloud notifications
236
+ await alarm.use_cloud_notifications(
237
+ # Optional, see note above redirecting cloud traffic from panel
238
+ local_port=5678,
239
+ upstream_host=None
240
+ )
241
+ # Start listening for notifications
242
+ await alarm.listen_notifications()
243
+
244
+
245
+ **Chained mode**
246
+
247
+ .. code:: python
248
+
249
+ from pyg90alarm import G90Alarm
250
+
251
+ # Create an instance of the alarm panel
252
+ alarm = G90Alarm(host='<panel IP address>')
253
+ # Enable cloud notifications
254
+ await alarm.use_cloud_notifications(
255
+ # Optional, see note above redirecting cloud traffic from panel
256
+ local_port=5678,
257
+ # See note above re: cloud service and sending packets to it
258
+ upstream_host='47.88.7.61',
259
+ upstream_port=5678
260
+ )
261
+ # Start listening for notifications
262
+ await alarm.listen_notifications()
263
+
264
+
265
+ Quick start
266
+ ===========
267
+
268
+ .. code:: shell
269
+
270
+ pip install pyg90alarm
271
+
272
+ Documentation
273
+ =============
274
+
275
+ Please see `online documentation <https://pyg90alarm.readthedocs.io>`_ for
276
+ details on the protocol, its security, supported commands and the API package
277
+ provides.
@@ -0,0 +1,230 @@
1
+ .. image:: https://github.com/hostcc/pyg90alarm/actions/workflows/main.yml/badge.svg?branch=master
2
+ :target: https://github.com/hostcc/pyg90alarm/tree/master
3
+ :alt: Github workflow status
4
+ .. image:: https://readthedocs.org/projects/pyg90alarm/badge/?version=stable
5
+ :target: https://pyg90alarm.readthedocs.io/en/stable
6
+ :alt: ReadTheDocs status
7
+ .. image:: https://img.shields.io/github/v/release/hostcc/pyg90alarm
8
+ :target: https://github.com/hostcc/pyg90alarm/releases/latest
9
+ :alt: Latest GitHub release
10
+ .. image:: https://img.shields.io/pypi/v/pyg90alarm
11
+ :target: https://pypi.org/project/pyg90alarm/
12
+ :alt: Latest PyPI version
13
+
14
+ Description
15
+ ===========
16
+
17
+ Python package to control G90-based alarm systems.
18
+
19
+ Many manufacturers sell such systems under different brands - Golden Security,
20
+ PST, Kerui and others. Those are cheap low-end systems, typically equipped with
21
+ WiFi and possible GSM interfaces for connectivity, and support different range
22
+ of peripherals:
23
+
24
+ * Wired and wireless sensors
25
+ * Relays (switches)
26
+
27
+ ... and probably others
28
+
29
+ The package implements asynchronous I/O over most of code paths using
30
+ `asyncio <https://docs.python.org/3/library/asyncio.html>`_.
31
+
32
+ Disclaimer
33
+ ==========
34
+
35
+ The author has no affiliation or any relationship to any of the hardware
36
+ vendors in question. The code has been created upon many trial and error
37
+ iterations.
38
+
39
+ Motivation
40
+ ==========
41
+
42
+ The primary motivation creating the code is the comfort of using the security
43
+ system - the mobile applications provided by the vendor, called "Carener", is
44
+ slow and crashes sometimes. Instead, it would be awesome to have the system
45
+ integrated into larger ecosystems, like Home Assistant, HomeKit and such.
46
+ Hence, the code has been created to interact with the security system using
47
+ Python, and it opens up a way for further integrations.
48
+
49
+ Supported hardware
50
+ ==================
51
+
52
+ It might not be possible to list every system supported by the package due to
53
+ manufacturers naming the products differently. Here is the list of hardware
54
+ known to work with the package:
55
+
56
+ * `PST G90B Plus <http://www.cameralarms.com/products/auto_dial_alarm_system/185.html>`_
57
+
58
+ And the list of sensors, actual set of device should be notable larger as many
59
+ of other manufacturers produce similar items. The names in parenthesis are
60
+ taken from the alarm system documentation, for example, `Home Alarm GB90-Plus <https://archive.org/details/HomeAlarmGB90-Plus/G90B%20plus%20WIFIGSMGPRS%20alarm%20system%20user%20manual/page/n7/mode/2up>`_.
61
+
62
+ * Wired PIR sensors
63
+ * Wireless PIR sensors (WPD01, WMS08)
64
+ * Door/window sensors (WDS07, WRDS01)
65
+ * Water leak sensors (LSTC01)
66
+ * Smoke sensors (WSD02)
67
+ * Gas sensors (WGD01)
68
+ * Switches/relays (JDQ)
69
+
70
+ Basically, the alarm system uses 433 MHz communications for the wireless
71
+ devices using EV1527, PT2262 protocols. The mobile application also mentions
72
+ some devices using 2.4GHz, although details of the protocols haven't been
73
+ identified as no such hardware has been available for experimentation.
74
+
75
+ Known caveats
76
+ =============
77
+
78
+ * Wireless shutter sensor (WRDS01) doesn't send anything on sensor closed, only
79
+ when opened. In contrast, WDS07 wireless door sensor does both.
80
+ * Wireless relays (at least JDQ) use same RF code for switching on and off,
81
+ when configured in toggle mode. That means a RF signal repeater will make
82
+ controlling such relays unpredictable, since the code will be sent more than
83
+ once.
84
+ * Low battery notifications for wireless sensors (at least for WDS07 and WSD02)
85
+ are often missing, either due to the sensors not sending them or the device
86
+ doesn't receive those.
87
+ * Wired sensors toggle on line state change, i.e. those aren't limited to have
88
+ normal closed (NC) or normal open (NO) contacts only. Best used with NC
89
+ contact sensors though, since an intruder cutting the line will trigger the
90
+ alarm.
91
+
92
+ Device notifications
93
+ ====================
94
+
95
+ Local notifications
96
+ -------------------
97
+
98
+ There is a hidden device capability to send protocol notifications over the
99
+ WiFi interface, thus called local. The notifications are done using broadcast UDP packets with source/destination ports being ``45000:12901`` (non-configurable), and sent when the device has IP address of its WiFi interface set to ``10.10.10.250``. That is the same IP the device will allocate to the WiFi interface when AP (access point is enabled). Please note enabling the AP *is not* required for the notifications to be sent, only the IP address matters. Likely the firmware does a check internally and enables those when corresponding IP address is found on the WiFi interface.
100
+
101
+ Depending on your network setup, ensuring the `10.10.10.250` IP address is
102
+ allocated to the WiFi interface of the device might be as simple as DHCP
103
+ reservation. Please check the documentation of your networking gear on how to
104
+ set the IP address allocation up.
105
+
106
+ .. note:: Since the IP address trick above isn't something the device exposes
107
+ to the user, the functionality might change or even cease functioning upon a
108
+ firmware upgrade!
109
+
110
+ .. note:: The device notifications in question are fully local with no
111
+ dependency on the cloud or Internet connection on the device.
112
+
113
+ .. note:: If IP address trick doesn't work for you by a reason, the package
114
+ will still be able to perform the key functions - for example, arming or
115
+ disarming the panel, or reading the list of sensors. However, the sensor
116
+ status will not be reflected and those will always be reported as inactive,
117
+ since there is no way to read their state in a polled manner.
118
+
119
+ To work that limitation around the package now supports simulating device
120
+ notifications from periodically polling the history it records - the
121
+ simulation works only for the alerts, not notifications (e.g. notifications
122
+ include low battery events and alike). This also requires the particular
123
+ alert to be enabled in the mobile application, otherwise it won't be
124
+ recorded in the history.
125
+
126
+ For the local notifications to be enabled the ``G90Alarm.use_local_notifications()`` needs to be called upon constructing an instance of ``G90Alarm`` class, then ``G90Alarm.listen_notifications()`` to start processing those coming from the panel - the code fragment below demonstrates that though being incomplete since callbacks (e.g. ``G90Alarm.on_armdisarm()``) should be set for the actual processing of the notifications.
127
+
128
+ .. code:: python
129
+
130
+ from pyg90alarm import G90Alarm
131
+
132
+ # Create an instance of the alarm panel
133
+ alarm = G90Alarm(host='10.10.10.250')
134
+ # Enable local notifications
135
+ await alarm.use_local_notifications()
136
+ # Start listening for notifications
137
+ await alarm.listen_notifications()
138
+
139
+ Cloud notifications
140
+ -------------------
141
+
142
+ The cloud protocol is native to the panel and is used to interact with mobile application. The package can mimic the cloud server and interpret the messages the panel sends to the cloud, allowing to receive the notifications and alerts.
143
+ While the protocol also allows to send commands to the panel, it is not implemented and local protocol is used for that - i.e. when cloud notifications are in use the local protocol still utilized for sending commands to the panel.
144
+
145
+ The cloud protocol is TCP based and typically interacts with cloud service at known IP address and port (not customizable at panel side). To process the cloud notifications all the traffic from panel towards the cloud (IP address ``47.88.7.61`` and TCP port ``5678`` as of writing) needs to be diverted to the node where the package is running - depending on your network equipment it could be port forwarding, DNAT or other means. It is unclear whether the panel utilizes DNS to resolve the cloud service IP address, hence the documentation only mentions IP-based traffic redirection.
146
+
147
+ Please see
148
+ `the section <docs/cloud-protocol.rst>`_ for further details on the protocol.
149
+
150
+ The benefit of the cloud notifications is that the panel no longer required to have ``10.10.10.250`` IP address.
151
+
152
+ The package could act as:
153
+
154
+ - Standalone cloud server with no Internet connectivity or cloud service
155
+ required at all - good if you'd like to avoid having a vendor service involved. Please note the mobile application will show panel as offline in this mode
156
+ - Chained cloud server, where in addition to interpreting the notifications it
157
+ will also forward all packets received from the panel to the cloud server, and pass its responses back to the panel. This allows to have notifications processed by the package and the mobile application working as well.
158
+
159
+ .. note:: Sending packets upstream to the known IP address and port of the cloud server might result in those looped back (since traffic from panel to cloud service has to be redirected to the host where package runs), if your network equipment can't account for source address in redirection rules (i.e. limiting the port redirection to the panel's IP address). In that case you'll need another redirection, from the host where the package runs to the cloud service using an IP from your network. That way those two redirection rules will coexist correctly. To illustrate:
160
+
161
+ Port forwarding rule 1:
162
+
163
+ - Source: panel IP address
164
+ - Destination: 47.88.7.61
165
+ - Port: 5678
166
+ - Redirect to host: host where package runs
167
+ - Redirect to port: 5678 (or other port if you want to use it)
168
+
169
+
170
+ Port forwarding rule 2 (optional):
171
+
172
+ - Source: host where package runs
173
+ - Destination: an IP address from your network
174
+ - Port: 5678 (or other port if you want to use it)
175
+ - Redirect to : 47.88.7.61
176
+ - Redirect to port: 5678
177
+
178
+ The code fragments below demonstrate how to utilize both modes - please note those are incomplete, since no callbacks are set to process the notifications.
179
+
180
+ **Standalone mode**
181
+
182
+ .. code:: python
183
+
184
+ from pyg90alarm import G90Alarm
185
+
186
+ # Create an instance of the alarm panel
187
+ alarm = G90Alarm(host='<panel IP address>')
188
+ # Enable cloud notifications
189
+ await alarm.use_cloud_notifications(
190
+ # Optional, see note above redirecting cloud traffic from panel
191
+ local_port=5678,
192
+ upstream_host=None
193
+ )
194
+ # Start listening for notifications
195
+ await alarm.listen_notifications()
196
+
197
+
198
+ **Chained mode**
199
+
200
+ .. code:: python
201
+
202
+ from pyg90alarm import G90Alarm
203
+
204
+ # Create an instance of the alarm panel
205
+ alarm = G90Alarm(host='<panel IP address>')
206
+ # Enable cloud notifications
207
+ await alarm.use_cloud_notifications(
208
+ # Optional, see note above redirecting cloud traffic from panel
209
+ local_port=5678,
210
+ # See note above re: cloud service and sending packets to it
211
+ upstream_host='47.88.7.61',
212
+ upstream_port=5678
213
+ )
214
+ # Start listening for notifications
215
+ await alarm.listen_notifications()
216
+
217
+
218
+ Quick start
219
+ ===========
220
+
221
+ .. code:: shell
222
+
223
+ pip install pyg90alarm
224
+
225
+ Documentation
226
+ =============
227
+
228
+ Please see `online documentation <https://pyg90alarm.readthedocs.io>`_ for
229
+ details on the protocol, its security, supported commands and the API package
230
+ provides.