pyg90alarm 2.3.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 +84 -0
- pyg90alarm/alarm.py +1274 -0
- pyg90alarm/callback.py +146 -0
- pyg90alarm/cloud/__init__.py +31 -0
- pyg90alarm/cloud/const.py +56 -0
- pyg90alarm/cloud/messages.py +593 -0
- pyg90alarm/cloud/notifications.py +410 -0
- pyg90alarm/cloud/protocol.py +518 -0
- pyg90alarm/const.py +273 -0
- pyg90alarm/definitions/__init__.py +3 -0
- pyg90alarm/definitions/base.py +247 -0
- pyg90alarm/definitions/devices.py +366 -0
- pyg90alarm/definitions/sensors.py +843 -0
- pyg90alarm/entities/__init__.py +3 -0
- pyg90alarm/entities/base_entity.py +93 -0
- pyg90alarm/entities/base_list.py +268 -0
- pyg90alarm/entities/device.py +97 -0
- pyg90alarm/entities/device_list.py +156 -0
- pyg90alarm/entities/sensor.py +891 -0
- pyg90alarm/entities/sensor_list.py +183 -0
- pyg90alarm/exceptions.py +63 -0
- pyg90alarm/local/__init__.py +0 -0
- pyg90alarm/local/base_cmd.py +293 -0
- pyg90alarm/local/config.py +157 -0
- pyg90alarm/local/discovery.py +103 -0
- pyg90alarm/local/history.py +272 -0
- pyg90alarm/local/host_info.py +89 -0
- pyg90alarm/local/host_status.py +52 -0
- pyg90alarm/local/notifications.py +117 -0
- pyg90alarm/local/paginated_cmd.py +132 -0
- pyg90alarm/local/paginated_result.py +135 -0
- pyg90alarm/local/targeted_discovery.py +162 -0
- pyg90alarm/local/user_data_crc.py +46 -0
- pyg90alarm/notifications/__init__.py +0 -0
- pyg90alarm/notifications/base.py +481 -0
- pyg90alarm/notifications/protocol.py +127 -0
- pyg90alarm/py.typed +0 -0
- pyg90alarm-2.3.0.dist-info/METADATA +277 -0
- pyg90alarm-2.3.0.dist-info/RECORD +42 -0
- pyg90alarm-2.3.0.dist-info/WHEEL +5 -0
- pyg90alarm-2.3.0.dist-info/licenses/LICENSE +21 -0
- pyg90alarm-2.3.0.dist-info/top_level.txt +1 -0
pyg90alarm/callback.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Copyright (c) 2021 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
|
+
Implements callbacks.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
import asyncio
|
|
26
|
+
from functools import (partial, wraps)
|
|
27
|
+
from asyncio import Task
|
|
28
|
+
from typing import (
|
|
29
|
+
Any, Callable, Coroutine, cast, Optional, Union, TypeVar, Generic
|
|
30
|
+
)
|
|
31
|
+
import logging
|
|
32
|
+
|
|
33
|
+
Callback = Optional[
|
|
34
|
+
Union[
|
|
35
|
+
Callable[..., None],
|
|
36
|
+
Callable[..., Coroutine[None, None, None]],
|
|
37
|
+
]
|
|
38
|
+
]
|
|
39
|
+
T = TypeVar('T', bound=Callback)
|
|
40
|
+
|
|
41
|
+
_LOGGER = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class G90Callback:
|
|
45
|
+
"""
|
|
46
|
+
Implements callbacks.
|
|
47
|
+
"""
|
|
48
|
+
@staticmethod
|
|
49
|
+
def invoke(
|
|
50
|
+
callback: Callback, *args: Any, **kwargs: Any
|
|
51
|
+
) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Invokes the callback.
|
|
54
|
+
"""
|
|
55
|
+
if not callback:
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
_LOGGER.debug('Attempting to invoke callback %s'
|
|
59
|
+
' (args: %s, kwargs: %s)',
|
|
60
|
+
callback, args, kwargs)
|
|
61
|
+
|
|
62
|
+
if not asyncio.iscoroutinefunction(callback):
|
|
63
|
+
def async_wrapper(
|
|
64
|
+
func: Callable[..., None]
|
|
65
|
+
) -> Callable[..., Coroutine[Any, Any, None]]:
|
|
66
|
+
"""
|
|
67
|
+
Wraps the regular callback function into coroutine, so it could
|
|
68
|
+
later be created as async task.
|
|
69
|
+
"""
|
|
70
|
+
@wraps(func)
|
|
71
|
+
async def wrapper(
|
|
72
|
+
*args: Any, **kwds: Any
|
|
73
|
+
) -> None:
|
|
74
|
+
return func(*args, **kwds)
|
|
75
|
+
|
|
76
|
+
return cast(Callable[..., Coroutine[Any, Any, None]], wrapper)
|
|
77
|
+
|
|
78
|
+
task = asyncio.create_task(
|
|
79
|
+
async_wrapper(
|
|
80
|
+
cast(Callable[..., None], callback)
|
|
81
|
+
)(*args, **kwargs)
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
task = asyncio.create_task(callback(*args, **kwargs))
|
|
85
|
+
|
|
86
|
+
def reap_callback_exception(task: Task[Any]) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Reaps an exception (if any) from the task logging it, to prevent
|
|
89
|
+
`asyncio` reporting that task exception was never retrieved.
|
|
90
|
+
"""
|
|
91
|
+
exc = task.exception()
|
|
92
|
+
if exc:
|
|
93
|
+
_LOGGER.error(
|
|
94
|
+
"Got exception when invoking callback '%s(...)':",
|
|
95
|
+
cast(
|
|
96
|
+
Coroutine[Any, Any, None], task.get_coro()
|
|
97
|
+
).__qualname__,
|
|
98
|
+
exc_info=exc, stack_info=False
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
task.add_done_callback(reap_callback_exception)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def invoke_delayed(
|
|
105
|
+
delay: float, callback: Callable[..., None], *args: Any, **kwargs: Any
|
|
106
|
+
) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Invokes the callback after a delay.
|
|
109
|
+
"""
|
|
110
|
+
loop = asyncio.get_running_loop()
|
|
111
|
+
loop.call_later(delay, partial(callback, *args, **kwargs))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class G90CallbackList(Generic[T]):
|
|
115
|
+
"""
|
|
116
|
+
Implements a list of callbacks.
|
|
117
|
+
"""
|
|
118
|
+
def __init__(self) -> None:
|
|
119
|
+
self._callbacks: list[T] = []
|
|
120
|
+
|
|
121
|
+
def add(self, callback: T) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Adds a callback to the list.
|
|
124
|
+
"""
|
|
125
|
+
if callback and callback not in self._callbacks:
|
|
126
|
+
self._callbacks.append(callback)
|
|
127
|
+
|
|
128
|
+
def remove(self, callback: T) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Removes a callback from the list.
|
|
131
|
+
"""
|
|
132
|
+
if callback in self._callbacks:
|
|
133
|
+
self._callbacks.remove(callback)
|
|
134
|
+
|
|
135
|
+
def clear(self) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Clears the list of callbacks.
|
|
138
|
+
"""
|
|
139
|
+
self._callbacks.clear()
|
|
140
|
+
|
|
141
|
+
def invoke(self, *args: Any, **kwargs: Any) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Invokes all callbacks in the list.
|
|
144
|
+
"""
|
|
145
|
+
for callback in self._callbacks:
|
|
146
|
+
G90Callback.invoke(callback, *args, **kwargs)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Copyright (c) 2025 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
|
+
Cloud communication implementation for G90 alarm systems.
|
|
23
|
+
|
|
24
|
+
This module provides the necessary components to interact with G90 alarm
|
|
25
|
+
systems through their cloud protocol (referred to as version 1.1).
|
|
26
|
+
"""
|
|
27
|
+
from .notifications import G90CloudNotifications # noqa: F401
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
'G90CloudNotifications',
|
|
31
|
+
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Copyright (c) 2025 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
|
+
Constants used in the G90 cloud protocol implementation.
|
|
23
|
+
|
|
24
|
+
This module defines the main enumerations for direction and command types
|
|
25
|
+
used in the cloud protocol communication with G90 alarm systems.
|
|
26
|
+
"""
|
|
27
|
+
from enum import IntEnum
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class G90CloudDirection(IntEnum):
|
|
31
|
+
"""
|
|
32
|
+
Defines the direction of G90 cloud protocol messages.
|
|
33
|
+
|
|
34
|
+
These values indicate whether messages are flowing from the cloud to the
|
|
35
|
+
device, from the device to the cloud, or are part of discovery processes.
|
|
36
|
+
"""
|
|
37
|
+
UNSPECIFIED = 0
|
|
38
|
+
CLOUD = 32 # 0x20
|
|
39
|
+
DEVICE = 16 # 0x10
|
|
40
|
+
DEVICE_DISCOVERY = 48 # 0x30
|
|
41
|
+
CLOUD_DISCOVERY = 208 # 0xD0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class G90CloudCommand(IntEnum):
|
|
45
|
+
"""
|
|
46
|
+
Defines the command types used in G90 cloud protocol messages.
|
|
47
|
+
|
|
48
|
+
These values identify the purpose of each cloud protocol message,
|
|
49
|
+
such as hello messages, notifications, commands, and status updates.
|
|
50
|
+
"""
|
|
51
|
+
HELLO = 0x01
|
|
52
|
+
HELLO_ACK = 0x41
|
|
53
|
+
NOTIFICATION = 0x22
|
|
54
|
+
STATUS_CHANGE = 0x21
|
|
55
|
+
HELLO_INFO = 0x63
|
|
56
|
+
COMMAND = 0x29
|