paradox-alarm-interface 3.4.0__tar.gz → 3.4.2.dev0__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.
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/PKG-INFO +5 -5
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/README.md +2 -2
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/__init__.py +1 -1
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/config.py +1 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/ip/connection.py +8 -7
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/protocols.py +17 -15
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/adapters.py +17 -12
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/basic.py +3 -2
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/async_message_manager.py +6 -4
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/handlers.py +9 -5
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/help.py +8 -5
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/paradox.py +56 -57
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/PKG-INFO +5 -5
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/requires.txt +1 -1
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/requirements.txt +1 -1
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/setup.cfg +2 -2
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/LICENSE +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/MANIFEST.in +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/connection.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/handler.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/ip/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/ip/commands.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/ip/parsers.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/ip/stun_session.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/serial_connection.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/console_scripts/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/console_scripts/ip150_connection_decrypt.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/console_scripts/pai_dump_memory.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/console_scripts/pai_run.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/element_type_container.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/enums.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/memory_storage.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/model.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/event.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/exceptions.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/common.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/adapters.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/event.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/models/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/models/evo192.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/models/evo48.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/models/evo96.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/models/evohd.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/panel.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/parsers.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/property.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/panel.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/parsers.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/event.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/panel.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/parsers.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/spectra_magellan/property.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/interface_manager.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/ip_interface/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/ip_interface/client_connection.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/ip_interface/interface.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/core.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/abstract_entity.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/alarm_control_panel.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/binary_sensors.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/device.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/factory.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/sensor.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/entities/switch.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/helpers.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/homeassistant.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/core.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/dummy.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/gsm.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/homeassistant_notifications.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/pushbullet.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/pushover.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/signal.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/crypto.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/ar.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/de.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/el.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/en.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/et.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/he.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/hu.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/pl.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/pt.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/ro.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/ru.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/encodings/charmaps/tr.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/event_filter.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/format.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/ps.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/stun.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/utils.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/main.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/parsers/__init__.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/parsers/status.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/SOURCES.txt +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/dependency_links.txt +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/entry_points.txt +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/top_level.txt +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox_alarm_interface.egg-info/zip-safe +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/pyproject.toml +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/tests/test_async_queue.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/tests/test_config.py +0 -0
- {paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/tests/test_paradox.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: paradox-alarm-interface
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.2.dev0
|
|
4
4
|
Summary: Interface to Paradox Alarm Panels
|
|
5
5
|
Home-page: https://github.com/ParadoxAlarmInterface/pai
|
|
6
6
|
Author: Jevgeni Kiski, João Paulo Barraca
|
|
@@ -18,14 +18,14 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
-
Requires-Python:
|
|
21
|
+
Requires-Python: <3.12,>=3.8
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: construct~=2.9.43
|
|
25
25
|
Requires-Dist: argparse>=1.4.0
|
|
26
26
|
Requires-Dist: python-slugify>=4.0.1
|
|
27
27
|
Requires-Dist: pytz>=2021.3
|
|
28
|
-
Requires-Dist: paho_mqtt
|
|
28
|
+
Requires-Dist: paho_mqtt<2,>=1.5.0
|
|
29
29
|
Requires-Dist: requests>=2.20.0
|
|
30
30
|
Requires-Dist: pyserial-asyncio>=0.4
|
|
31
31
|
Provides-Extra: yaml
|
|
@@ -107,8 +107,8 @@ See [wiki](https://github.com/ParadoxAlarmInterface/pai/wiki/Installation)
|
|
|
107
107
|
## Tested Environment
|
|
108
108
|
|
|
109
109
|
Tested in the following environment:
|
|
110
|
-
* Python 3.6, 3.7, 3.8
|
|
111
|
-
* Mosquitto MQTT Broker >
|
|
110
|
+
* Python 3.6, 3.7, 3.8, 3.9, 3.10, 3.11
|
|
111
|
+
* Mosquitto MQTT Broker > 1.4.8
|
|
112
112
|
* OrangePi 2G-IOT, NanoPi NEO, and Raspberry Pi 3 through their built in Serial Port (with a level shifter!), or a USB RS232 TTL adapter (CP2102, PL2303, CH340, etc..)
|
|
113
113
|
* Ubuntu Server 16.04.3 LTS
|
|
114
114
|
* Paradox MG5050, SP7000 and EVO panels
|
|
@@ -65,8 +65,8 @@ See [wiki](https://github.com/ParadoxAlarmInterface/pai/wiki/Installation)
|
|
|
65
65
|
## Tested Environment
|
|
66
66
|
|
|
67
67
|
Tested in the following environment:
|
|
68
|
-
* Python 3.6, 3.7, 3.8
|
|
69
|
-
* Mosquitto MQTT Broker >
|
|
68
|
+
* Python 3.6, 3.7, 3.8, 3.9, 3.10, 3.11
|
|
69
|
+
* Mosquitto MQTT Broker > 1.4.8
|
|
70
70
|
* OrangePi 2G-IOT, NanoPi NEO, and Raspberry Pi 3 through their built in Serial Port (with a level shifter!), or a USB RS232 TTL adapter (CP2102, PL2303, CH340, etc..)
|
|
71
71
|
* Ubuntu Server 16.04.3 LTS
|
|
72
72
|
* Paradox MG5050, SP7000 and EVO panels
|
|
@@ -55,6 +55,7 @@ class Config:
|
|
|
55
55
|
"IP_CONNECTION_BARE": False, # IP endpoint connects directly to panel. Used for Serial Tunnels over TCP
|
|
56
56
|
# Paradox
|
|
57
57
|
"KEEP_ALIVE_INTERVAL": 10, # Interval between status updates
|
|
58
|
+
"IO_TIMEOUT": 0.5, # Timeout for IO operations
|
|
58
59
|
"LIMITS": {}, # By default all zones will be monitored
|
|
59
60
|
"LABEL_ENCODING": "paradox-en", # Encoding to use when decoding labels. paradox-* or https://docs.python.org/3/library/codecs.html#standard-encodings
|
|
60
61
|
"LABEL_REFRESH_INTERVAL": (
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
3
2
|
import asyncio
|
|
4
3
|
import logging
|
|
5
|
-
from abc import ABC, abstractmethod
|
|
6
4
|
|
|
7
5
|
from construct import Container
|
|
8
6
|
|
|
7
|
+
from paradox.config import config as cfg
|
|
9
8
|
from paradox.connections.connection import Connection
|
|
10
9
|
from paradox.connections.handler import IPConnectionHandler
|
|
11
10
|
from paradox.connections.ip.commands import IPModuleConnectCommand
|
|
12
11
|
from paradox.connections.ip.stun_session import StunSession
|
|
13
|
-
from paradox.connections.protocols import
|
|
14
|
-
SerialConnectionProtocol)
|
|
12
|
+
from paradox.connections.protocols import IPConnectionProtocol, SerialConnectionProtocol
|
|
15
13
|
from paradox.exceptions import PAICriticalException
|
|
16
14
|
from paradox.lib.handlers import FutureHandler, HandlerRegistry
|
|
17
15
|
|
|
@@ -94,7 +92,7 @@ class IPConnectionWithEncryption(MultiAttemptConnection, IPConnectionHandler, AB
|
|
|
94
92
|
def on_ip_message(self, container: Container):
|
|
95
93
|
return self.loop.create_task(self.ip_handler_registry.handle(container))
|
|
96
94
|
|
|
97
|
-
async def wait_for_ip_message(self, timeout=
|
|
95
|
+
async def wait_for_ip_message(self, timeout=cfg.IO_TIMEOUT) -> Container:
|
|
98
96
|
future = FutureHandler()
|
|
99
97
|
return await self.ip_handler_registry.wait_until_complete(future, timeout)
|
|
100
98
|
|
|
@@ -107,7 +105,10 @@ class IPConnectionWithEncryption(MultiAttemptConnection, IPConnectionHandler, AB
|
|
|
107
105
|
|
|
108
106
|
class LocalIPConnection(IPConnectionWithEncryption):
|
|
109
107
|
def __init__(
|
|
110
|
-
self,
|
|
108
|
+
self,
|
|
109
|
+
host,
|
|
110
|
+
port,
|
|
111
|
+
password,
|
|
111
112
|
):
|
|
112
113
|
super().__init__(password)
|
|
113
114
|
self.host = host
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
1
2
|
import asyncio
|
|
2
3
|
import binascii
|
|
3
4
|
import logging
|
|
4
|
-
from abc import abstractmethod
|
|
5
5
|
|
|
6
6
|
from paradox.config import config as cfg
|
|
7
7
|
from paradox.connections.handler import ConnectionHandler, IPConnectionHandler
|
|
8
|
-
from paradox.connections.ip.parsers import (
|
|
9
|
-
|
|
8
|
+
from paradox.connections.ip.parsers import (
|
|
9
|
+
IPMessageCommand,
|
|
10
|
+
IPMessageRequest,
|
|
11
|
+
IPMessageResponse,
|
|
12
|
+
IPMessageType,
|
|
13
|
+
)
|
|
10
14
|
|
|
11
15
|
logger = logging.getLogger("PAI").getChild(__name__)
|
|
12
16
|
|
|
@@ -52,11 +56,11 @@ class ConnectionProtocol(asyncio.Protocol):
|
|
|
52
56
|
if self.transport:
|
|
53
57
|
try:
|
|
54
58
|
self.transport.close()
|
|
55
|
-
except:
|
|
59
|
+
except Exception:
|
|
56
60
|
logger.exception("Connection transport close raised Exception")
|
|
57
61
|
self.transport = None
|
|
58
62
|
|
|
59
|
-
await asyncio.wait_for(self._closed, timeout=
|
|
63
|
+
await asyncio.wait_for(self._closed, timeout=cfg.IO_TIMEOUT)
|
|
60
64
|
|
|
61
65
|
@abstractmethod
|
|
62
66
|
def send_message(self, message):
|
|
@@ -94,7 +98,7 @@ class ConnectionProtocol(asyncio.Protocol):
|
|
|
94
98
|
class SerialConnectionProtocol(ConnectionProtocol):
|
|
95
99
|
def send_message(self, message):
|
|
96
100
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
97
|
-
logger.debug("PAI -> SER {
|
|
101
|
+
logger.debug(f"PAI -> SER {binascii.hexlify(message)}")
|
|
98
102
|
|
|
99
103
|
self.check_active()
|
|
100
104
|
|
|
@@ -137,7 +141,7 @@ class SerialConnectionProtocol(ConnectionProtocol):
|
|
|
137
141
|
if checksum(frame, min_length):
|
|
138
142
|
self.buffer = self.buffer[len(frame) :] # Remove message
|
|
139
143
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
140
|
-
logger.debug("SER -> PAI {
|
|
144
|
+
logger.debug(f"SER -> PAI {binascii.hexlify(frame)}")
|
|
141
145
|
|
|
142
146
|
self.handler.on_message(frame)
|
|
143
147
|
else:
|
|
@@ -146,14 +150,14 @@ class SerialConnectionProtocol(ConnectionProtocol):
|
|
|
146
150
|
|
|
147
151
|
class IPConnectionProtocol(ConnectionProtocol):
|
|
148
152
|
def __init__(self, handler: IPConnectionHandler, key):
|
|
149
|
-
super(
|
|
153
|
+
super().__init__(handler)
|
|
150
154
|
|
|
151
155
|
self.handler = handler
|
|
152
156
|
self.key = key
|
|
153
157
|
|
|
154
158
|
def send_raw(self, raw):
|
|
155
159
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
156
|
-
logger.debug("PAI -> IP (raw) {
|
|
160
|
+
logger.debug(f"PAI -> IP (raw) {binascii.hexlify(raw)}")
|
|
157
161
|
|
|
158
162
|
self.check_active()
|
|
159
163
|
|
|
@@ -161,7 +165,7 @@ class IPConnectionProtocol(ConnectionProtocol):
|
|
|
161
165
|
|
|
162
166
|
def send_message(self, message):
|
|
163
167
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
164
|
-
logger.debug("PAI -> IP (payload) {
|
|
168
|
+
logger.debug(f"PAI -> IP (payload) {binascii.hexlify(message)}")
|
|
165
169
|
|
|
166
170
|
self.check_active()
|
|
167
171
|
|
|
@@ -180,7 +184,7 @@ class IPConnectionProtocol(ConnectionProtocol):
|
|
|
180
184
|
password=self.key,
|
|
181
185
|
)
|
|
182
186
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
183
|
-
logger.debug("PAI -> IP (raw) {
|
|
187
|
+
logger.debug(f"PAI -> IP (raw) {binascii.hexlify(msg)}")
|
|
184
188
|
|
|
185
189
|
self.transport.write(msg)
|
|
186
190
|
|
|
@@ -188,9 +192,7 @@ class IPConnectionProtocol(ConnectionProtocol):
|
|
|
188
192
|
message = IPMessageResponse.parse(data, password=self.key)
|
|
189
193
|
|
|
190
194
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
191
|
-
logger.debug(
|
|
192
|
-
"IP -> PAI (payload) {}".format(binascii.hexlify(message.payload))
|
|
193
|
-
)
|
|
195
|
+
logger.debug(f"IP -> PAI (payload) {binascii.hexlify(message.payload)}")
|
|
194
196
|
|
|
195
197
|
if message.header.message_type == IPMessageType.serial_passthrough_response:
|
|
196
198
|
self.handler.on_message(message.payload)
|
|
@@ -218,7 +220,7 @@ class IPConnectionProtocol(ConnectionProtocol):
|
|
|
218
220
|
return
|
|
219
221
|
|
|
220
222
|
if cfg.LOGGING_DUMP_PACKETS:
|
|
221
|
-
logger.debug("IP -> PAI (raw) {
|
|
223
|
+
logger.debug(f"IP -> PAI (raw) {binascii.hexlify(self.buffer)}")
|
|
222
224
|
|
|
223
225
|
self._process_message(self.buffer)
|
|
224
226
|
self.buffer = b""
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
1
|
import datetime
|
|
2
|
+
from enum import Enum
|
|
4
3
|
|
|
5
4
|
from construct import Adapter
|
|
6
|
-
|
|
5
|
+
|
|
7
6
|
|
|
8
7
|
class DateAdapter(Adapter):
|
|
9
8
|
def _decode(self, obj, context, path):
|
|
@@ -35,7 +34,6 @@ class PartitionStateAdapter(Adapter):
|
|
|
35
34
|
states = dict(arm=4, disarm=5, arm_sleep=3, arm_stay=1, none=0)
|
|
36
35
|
|
|
37
36
|
def _decode(self, obj, context, path):
|
|
38
|
-
|
|
39
37
|
for k, v in enumerate(self.states):
|
|
40
38
|
if v == obj[0]:
|
|
41
39
|
return k
|
|
@@ -53,7 +51,6 @@ class ZoneStateAdapter(Adapter):
|
|
|
53
51
|
states = dict(bypass=0x10)
|
|
54
52
|
|
|
55
53
|
def _decode(self, obj, context, path):
|
|
56
|
-
|
|
57
54
|
for k, v in enumerate(self.states):
|
|
58
55
|
if v == obj[0]:
|
|
59
56
|
return k
|
|
@@ -227,16 +224,24 @@ class PGMDefinitionAdapter(Adapter):
|
|
|
227
224
|
disabled = sum(obj) == 0
|
|
228
225
|
|
|
229
226
|
if disabled:
|
|
230
|
-
return
|
|
227
|
+
return "disabled"
|
|
231
228
|
|
|
232
229
|
try:
|
|
233
|
-
activation_event =
|
|
234
|
-
|
|
235
|
-
|
|
230
|
+
activation_event = (
|
|
231
|
+
PGMDefinitionAdapter.PGMEvent(obj[0])
|
|
232
|
+
if sum(obj[:3]) != 0
|
|
233
|
+
else "disabled"
|
|
234
|
+
)
|
|
235
|
+
except Exception:
|
|
236
|
+
activation_event = "unknown"
|
|
236
237
|
|
|
237
238
|
try:
|
|
238
|
-
deactivation_event =
|
|
239
|
-
|
|
240
|
-
|
|
239
|
+
deactivation_event = (
|
|
240
|
+
PGMDefinitionAdapter.PGMEvent(obj[3])
|
|
241
|
+
if sum(obj[3:]) != 0
|
|
242
|
+
else "disabled"
|
|
243
|
+
)
|
|
244
|
+
except Exception:
|
|
245
|
+
deactivation_event = "unknown"
|
|
241
246
|
|
|
242
247
|
return dict(activation=activation_event, deactivation=deactivation_event)
|
|
@@ -461,7 +461,8 @@ class BasicMQTTInterface(AbstractMQTTInterface):
|
|
|
461
461
|
if i not in labels:
|
|
462
462
|
continue
|
|
463
463
|
|
|
464
|
-
|
|
464
|
+
definition = {**(definitions[i]), "id": i}
|
|
465
|
+
for attribute in definition: # attribute
|
|
465
466
|
if element_type == "user" and attribute == "code":
|
|
466
467
|
continue
|
|
467
468
|
self._publish(
|
|
@@ -469,7 +470,7 @@ class BasicMQTTInterface(AbstractMQTTInterface):
|
|
|
469
470
|
element_type,
|
|
470
471
|
labels[i]["key"],
|
|
471
472
|
attribute,
|
|
472
|
-
|
|
473
|
+
definition[attribute],
|
|
473
474
|
)
|
|
474
475
|
|
|
475
476
|
if (
|
|
@@ -4,8 +4,8 @@ from typing import Callable, Optional
|
|
|
4
4
|
|
|
5
5
|
from construct import Container
|
|
6
6
|
|
|
7
|
-
from paradox.
|
|
8
|
-
|
|
7
|
+
from paradox.config import config as cfg
|
|
8
|
+
from paradox.lib.handlers import FutureHandler, HandlerRegistry, PersistentHandler
|
|
9
9
|
|
|
10
10
|
logger = logging.getLogger("PAI").getChild(__name__)
|
|
11
11
|
|
|
@@ -27,7 +27,7 @@ class ErrorMessageHandler(PersistentHandler):
|
|
|
27
27
|
|
|
28
28
|
class AsyncMessageManager:
|
|
29
29
|
def __init__(self, loop=None):
|
|
30
|
-
super(
|
|
30
|
+
super().__init__()
|
|
31
31
|
|
|
32
32
|
if not loop:
|
|
33
33
|
loop = asyncio.get_event_loop()
|
|
@@ -37,7 +37,9 @@ class AsyncMessageManager:
|
|
|
37
37
|
self.raw_handler_registry = HandlerRegistry()
|
|
38
38
|
|
|
39
39
|
async def wait_for_message(
|
|
40
|
-
self,
|
|
40
|
+
self,
|
|
41
|
+
check_fn: Optional[Callable[[Container], bool]] = None,
|
|
42
|
+
timeout=cfg.IO_TIMEOUT,
|
|
41
43
|
) -> Container:
|
|
42
44
|
return await self.handler_registry.wait_until_complete(
|
|
43
45
|
FutureHandler(check_fn), timeout
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/handlers.py
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
1
2
|
import asyncio
|
|
2
3
|
import logging
|
|
3
|
-
from abc import abstractmethod
|
|
4
4
|
from typing import Any, Awaitable, Callable, List, Optional, Union
|
|
5
5
|
|
|
6
|
+
from paradox.config import config as cfg
|
|
7
|
+
|
|
6
8
|
logger = logging.getLogger("PAI").getChild(__name__)
|
|
7
9
|
|
|
8
10
|
|
|
@@ -30,7 +32,9 @@ class AlreadyHandledError(Exception):
|
|
|
30
32
|
|
|
31
33
|
class FutureHandler(Handler, asyncio.Future):
|
|
32
34
|
def __init__(
|
|
33
|
-
self,
|
|
35
|
+
self,
|
|
36
|
+
check_fn: Optional[Callable[[Any], bool]] = None,
|
|
37
|
+
name=None,
|
|
34
38
|
):
|
|
35
39
|
super().__init__()
|
|
36
40
|
self.name = name if name is not None else self.__class__.__name__
|
|
@@ -53,7 +57,7 @@ class PersistentHandler(Handler):
|
|
|
53
57
|
def __init__(
|
|
54
58
|
self, callback: Callable[[Any], Union[None, Awaitable[None]]], name=None
|
|
55
59
|
):
|
|
56
|
-
super(
|
|
60
|
+
super().__init__(name)
|
|
57
61
|
self._handle = callback
|
|
58
62
|
self.persistent = True
|
|
59
63
|
|
|
@@ -90,7 +94,7 @@ class HandlerRegistry:
|
|
|
90
94
|
for handler in to_remove:
|
|
91
95
|
self.remove(handler)
|
|
92
96
|
|
|
93
|
-
async def wait_until_complete(self, handler: Handler, timeout=
|
|
97
|
+
async def wait_until_complete(self, handler: Handler, timeout=cfg.IO_TIMEOUT):
|
|
94
98
|
self.append(handler)
|
|
95
99
|
try:
|
|
96
100
|
return await asyncio.wait_for(handler, timeout=timeout)
|
|
@@ -112,7 +116,7 @@ class HandlerRegistry:
|
|
|
112
116
|
await handler(data)
|
|
113
117
|
except AlreadyHandledError:
|
|
114
118
|
logger.error("Already handled")
|
|
115
|
-
except:
|
|
119
|
+
except Exception:
|
|
116
120
|
logger.exception("Exception caught during message handling")
|
|
117
121
|
raise
|
|
118
122
|
|
|
@@ -33,7 +33,9 @@ MODULES = {
|
|
|
33
33
|
"ws4py": dict(
|
|
34
34
|
mandatory=False, desc="the Pushbullet interface", install_name="ws4py>=0.4.2"
|
|
35
35
|
),
|
|
36
|
-
"yaml": dict(
|
|
36
|
+
"yaml": dict(
|
|
37
|
+
mandatory=False, desc="the IP150 connection", install_name="pyyaml>=5.2.0"
|
|
38
|
+
),
|
|
37
39
|
"chump": dict(
|
|
38
40
|
mandatory=False, desc="the Pushover interface", install_name="chump>=1.6.0"
|
|
39
41
|
),
|
|
@@ -44,9 +46,10 @@ MODULES = {
|
|
|
44
46
|
mandatory=False, desc="the Signal interface", install_name="pygobject>=3.20.0"
|
|
45
47
|
),
|
|
46
48
|
"gi": dict(mandatory=False, desc="the Signal interface", install_name="gi>=1.2"),
|
|
47
|
-
"pytz": dict(
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
"pytz": dict(
|
|
50
|
+
mandatory=False, desc="Panel time sync", install_name="pytz>=2023.3.post1"
|
|
51
|
+
),
|
|
52
|
+
"mqtt": dict(mandatory=False, desc="MQTT", install_name="paho_mqtt>=1.5.0,<2"),
|
|
50
53
|
"pre-commit": dict(mandatory=False, desc="Development", install_name="pre-commit"),
|
|
51
54
|
"flake8": dict(mandatory=False, desc="Code checker", install_name="flake8"),
|
|
52
55
|
"tox": dict(mandatory=False, desc="virtual env / testing", install_name="tox"),
|
|
@@ -54,7 +57,7 @@ MODULES = {
|
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
def import_error_help(error):
|
|
57
|
-
logger.error("Could not import Python3 module '{}': {}\n"
|
|
60
|
+
logger.error(f"Could not import Python3 module '{error.name}': {error}\n")
|
|
58
61
|
|
|
59
62
|
if error.name in MODULES:
|
|
60
63
|
m = MODULES[error.name]
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
1
|
import asyncio
|
|
4
|
-
import logging
|
|
5
|
-
import time
|
|
6
|
-
import pytz
|
|
7
2
|
from binascii import hexlify
|
|
8
3
|
from datetime import datetime
|
|
9
|
-
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
from typing import Callable, Iterable, Optional
|
|
10
7
|
|
|
11
8
|
from construct import Container
|
|
9
|
+
import pytz
|
|
12
10
|
|
|
13
11
|
from paradox.config import config as cfg, get_limits_for_type
|
|
14
12
|
from paradox.connections.connection import Connection
|
|
@@ -16,13 +14,16 @@ from paradox.data.enums import RunState
|
|
|
16
14
|
from paradox.data.memory_storage import MemoryStorage as Storage
|
|
17
15
|
from paradox.data.model import DetectedPanel
|
|
18
16
|
from paradox.event import Change, ChangeEvent, Event, LiveEvent
|
|
19
|
-
from paradox.exceptions import (
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
from paradox.exceptions import (
|
|
18
|
+
AuthenticationFailed,
|
|
19
|
+
CodeLockout,
|
|
20
|
+
PanelNotDetected,
|
|
21
|
+
StatusRequestException,
|
|
22
|
+
async_loop_unhandled_exception_handler,
|
|
23
|
+
)
|
|
22
24
|
from paradox.hardware import Panel, create_panel
|
|
23
25
|
from paradox.lib import ps
|
|
24
|
-
from paradox.lib.async_message_manager import
|
|
25
|
-
EventMessageHandler)
|
|
26
|
+
from paradox.lib.async_message_manager import ErrorMessageHandler, EventMessageHandler
|
|
26
27
|
from paradox.lib.handlers import PersistentHandler
|
|
27
28
|
from paradox.lib.utils import deep_merge
|
|
28
29
|
from paradox.parsers.status import convert_raw_status
|
|
@@ -35,7 +36,7 @@ class Paradox:
|
|
|
35
36
|
self.panel: Panel = None
|
|
36
37
|
self._connection: Connection = None
|
|
37
38
|
self.retries = retries
|
|
38
|
-
self.work_loop = asyncio.get_event_loop()
|
|
39
|
+
self.work_loop = asyncio.get_event_loop()
|
|
39
40
|
self.work_loop.set_exception_handler(async_loop_unhandled_exception_handler)
|
|
40
41
|
|
|
41
42
|
self.storage = Storage()
|
|
@@ -70,7 +71,8 @@ class Paradox:
|
|
|
70
71
|
from paradox.connections.serial_connection import SerialCommunication
|
|
71
72
|
|
|
72
73
|
self._connection = SerialCommunication(
|
|
73
|
-
port=cfg.SERIAL_PORT,
|
|
74
|
+
port=cfg.SERIAL_PORT,
|
|
75
|
+
baud=cfg.SERIAL_BAUD,
|
|
74
76
|
)
|
|
75
77
|
elif cfg.CONNECTION_TYPE == "IP":
|
|
76
78
|
logger.info("Using IP Connection")
|
|
@@ -81,10 +83,7 @@ class Paradox:
|
|
|
81
83
|
self._connection = BareIPConnection(
|
|
82
84
|
host=cfg.IP_CONNECTION_HOST, port=cfg.IP_CONNECTION_PORT
|
|
83
85
|
)
|
|
84
|
-
elif
|
|
85
|
-
cfg.IP_CONNECTION_SITEID
|
|
86
|
-
and cfg.IP_CONNECTION_EMAIL
|
|
87
|
-
):
|
|
86
|
+
elif cfg.IP_CONNECTION_SITEID and cfg.IP_CONNECTION_EMAIL:
|
|
88
87
|
from paradox.connections.ip.connection import StunIPConnection
|
|
89
88
|
|
|
90
89
|
self._connection = StunIPConnection(
|
|
@@ -102,9 +101,7 @@ class Paradox:
|
|
|
102
101
|
password=cfg.IP_CONNECTION_PASSWORD,
|
|
103
102
|
)
|
|
104
103
|
else:
|
|
105
|
-
raise AssertionError(
|
|
106
|
-
"Invalid connection type: {}".format(cfg.CONNECTION_TYPE)
|
|
107
|
-
)
|
|
104
|
+
raise AssertionError(f"Invalid connection type: {cfg.CONNECTION_TYPE}")
|
|
108
105
|
|
|
109
106
|
self._register_connection_handlers()
|
|
110
107
|
|
|
@@ -140,7 +137,7 @@ class Paradox:
|
|
|
140
137
|
initiate_reply = await self.send_wait(
|
|
141
138
|
self.panel.get_message("InitiateCommunication"),
|
|
142
139
|
None,
|
|
143
|
-
reply_expected=0x7
|
|
140
|
+
reply_expected=0x7,
|
|
144
141
|
)
|
|
145
142
|
|
|
146
143
|
if initiate_reply:
|
|
@@ -156,9 +153,7 @@ class Paradox:
|
|
|
156
153
|
initiate_reply.fields.value.serial_number
|
|
157
154
|
).decode()
|
|
158
155
|
|
|
159
|
-
logger.info(
|
|
160
|
-
"Panel Identified {} version {}".format(model, firmware_version)
|
|
161
|
-
)
|
|
156
|
+
logger.info(f"Panel Identified {model} version {firmware_version}")
|
|
162
157
|
else:
|
|
163
158
|
raise ConnectionError("Panel did not replied to InitiateCommunication")
|
|
164
159
|
|
|
@@ -251,7 +246,9 @@ class Paradox:
|
|
|
251
246
|
tzinfo = pytz.timezone(cfg.SYNC_TIME_TIMEZONE)
|
|
252
247
|
now = now.astimezone(tzinfo)
|
|
253
248
|
except pytz.exceptions.UnknownTimeZoneError:
|
|
254
|
-
logger.debug(
|
|
249
|
+
logger.debug(
|
|
250
|
+
f"Panel Timezone Unknown ('{cfg.SYNC_TIME_TIMEZONE}'). Skipping sync"
|
|
251
|
+
)
|
|
255
252
|
return
|
|
256
253
|
|
|
257
254
|
if not self._is_time_sync_required(now.replace(tzinfo=None)):
|
|
@@ -297,13 +294,13 @@ class Paradox:
|
|
|
297
294
|
logger.error("Lost communication with panel")
|
|
298
295
|
await self.disconnect()
|
|
299
296
|
return
|
|
300
|
-
except:
|
|
297
|
+
except Exception:
|
|
301
298
|
logger.exception("Loop")
|
|
302
299
|
finally:
|
|
303
300
|
self.busy.release()
|
|
304
301
|
|
|
305
302
|
if replies_missing > 0:
|
|
306
|
-
logger.debug("Loop: Replies missing: {}"
|
|
303
|
+
logger.debug(f"Loop: Replies missing: {replies_missing}")
|
|
307
304
|
|
|
308
305
|
# cfg.Listen for events
|
|
309
306
|
|
|
@@ -353,15 +350,14 @@ class Paradox:
|
|
|
353
350
|
# No message
|
|
354
351
|
if recv_message is None:
|
|
355
352
|
logger.debug(
|
|
356
|
-
"Unknown message: %s"
|
|
357
|
-
% (" ".join("{:02x} ".format(c) for c in message))
|
|
353
|
+
"Unknown message: %s" % (" ".join(f"{c:02x} " for c in message))
|
|
358
354
|
)
|
|
359
355
|
return
|
|
360
356
|
|
|
361
357
|
self.connection.schedule_message_handling(
|
|
362
358
|
recv_message
|
|
363
359
|
) # schedule handling in the loop
|
|
364
|
-
except:
|
|
360
|
+
except Exception:
|
|
365
361
|
logger.exception("Error parsing message")
|
|
366
362
|
|
|
367
363
|
async def send_wait(
|
|
@@ -370,10 +366,9 @@ class Paradox:
|
|
|
370
366
|
args=None,
|
|
371
367
|
message=None,
|
|
372
368
|
retries=5,
|
|
373
|
-
timeout=
|
|
369
|
+
timeout=cfg.IO_TIMEOUT,
|
|
374
370
|
reply_expected=None,
|
|
375
371
|
) -> Optional[Container]:
|
|
376
|
-
|
|
377
372
|
# Connection closed
|
|
378
373
|
if not self.connection.connected:
|
|
379
374
|
raise ConnectionError("Not connected")
|
|
@@ -422,12 +417,12 @@ class Paradox:
|
|
|
422
417
|
except ConnectionError:
|
|
423
418
|
result = "connection error"
|
|
424
419
|
raise
|
|
425
|
-
except:
|
|
420
|
+
except Exception:
|
|
426
421
|
result = "exception"
|
|
427
422
|
logger.exception("Unexpected exception during send_wait")
|
|
428
423
|
raise
|
|
429
|
-
|
|
430
|
-
|
|
424
|
+
finally:
|
|
425
|
+
logger.debug("send/receive %s in %.4f s", result, time.time() - t1)
|
|
431
426
|
|
|
432
427
|
attempt += 1
|
|
433
428
|
|
|
@@ -435,11 +430,9 @@ class Paradox:
|
|
|
435
430
|
|
|
436
431
|
async def control_zone(self, zone: str, command: str) -> bool:
|
|
437
432
|
command = command.lower()
|
|
438
|
-
logger.debug("Control Zone: {} - {}"
|
|
433
|
+
logger.debug(f"Control Zone: {zone} - {command}")
|
|
439
434
|
|
|
440
|
-
zones_selected = self.storage.get_container("zone").select(
|
|
441
|
-
zone
|
|
442
|
-
) # type: Sequence[int]
|
|
435
|
+
zones_selected = self.storage.get_container("zone").select(zone)
|
|
443
436
|
|
|
444
437
|
# Not Found
|
|
445
438
|
if len(zones_selected) == 0:
|
|
@@ -464,11 +457,9 @@ class Paradox:
|
|
|
464
457
|
|
|
465
458
|
async def control_partition(self, partition: str, command: str) -> bool:
|
|
466
459
|
command = command.lower()
|
|
467
|
-
logger.debug("Control Partition: {} - {}"
|
|
460
|
+
logger.debug(f"Control Partition: {partition} - {command}")
|
|
468
461
|
|
|
469
|
-
partitions_selected = self.storage.get_container("partition").select(
|
|
470
|
-
partition
|
|
471
|
-
) # type: Sequence[int]
|
|
462
|
+
partitions_selected = self.storage.get_container("partition").select(partition)
|
|
472
463
|
|
|
473
464
|
# Not Found
|
|
474
465
|
if len(partitions_selected) == 0:
|
|
@@ -493,7 +484,7 @@ class Paradox:
|
|
|
493
484
|
|
|
494
485
|
async def control_output(self, output, command) -> bool:
|
|
495
486
|
command = command.lower()
|
|
496
|
-
logger.debug("Control Output: {} - {}"
|
|
487
|
+
logger.debug(f"Control Output: {output} - {command}")
|
|
497
488
|
|
|
498
489
|
outputs_selected = self.storage.get_container("pgm").select(output)
|
|
499
490
|
|
|
@@ -545,7 +536,7 @@ class Paradox:
|
|
|
545
536
|
|
|
546
537
|
async def control_door(self, door, command) -> bool:
|
|
547
538
|
command = command.lower()
|
|
548
|
-
logger.debug("Control Door: {} - {}"
|
|
539
|
+
logger.debug(f"Control Door: {door} - {command}")
|
|
549
540
|
|
|
550
541
|
doors_selected = self.storage.get_container("door").select(door)
|
|
551
542
|
|
|
@@ -585,7 +576,7 @@ class Paradox:
|
|
|
585
576
|
event_map=self.panel.event_map,
|
|
586
577
|
label_provider=self.get_label,
|
|
587
578
|
)
|
|
588
|
-
except AssertionError
|
|
579
|
+
except AssertionError:
|
|
589
580
|
logger.debug("Error creating event")
|
|
590
581
|
return
|
|
591
582
|
|
|
@@ -633,7 +624,7 @@ class Paradox:
|
|
|
633
624
|
|
|
634
625
|
ps.sendEvent(evt)
|
|
635
626
|
|
|
636
|
-
except:
|
|
627
|
+
except Exception:
|
|
637
628
|
logger.exception("Handle live event")
|
|
638
629
|
|
|
639
630
|
def handle_error_message(self, message):
|
|
@@ -644,7 +635,7 @@ class Paradox:
|
|
|
644
635
|
asyncio.get_event_loop().create_task(self.disconnect())
|
|
645
636
|
else:
|
|
646
637
|
message = self.panel.get_error_message(error_enum)
|
|
647
|
-
logger.error("Got ERROR Message: {}"
|
|
638
|
+
logger.error(f"Got ERROR Message: {message}")
|
|
648
639
|
if message == "Invalid PC Password":
|
|
649
640
|
raise AuthenticationFailed()
|
|
650
641
|
elif "code lockout" in message:
|
|
@@ -709,7 +700,13 @@ class Paradox:
|
|
|
709
700
|
if element_type in ["troubles"]: # troubles was already parsed
|
|
710
701
|
continue
|
|
711
702
|
for element_item_key, element_item_status in element_items.items():
|
|
712
|
-
if isinstance(
|
|
703
|
+
if isinstance(
|
|
704
|
+
element_item_status,
|
|
705
|
+
(
|
|
706
|
+
dict,
|
|
707
|
+
list,
|
|
708
|
+
),
|
|
709
|
+
):
|
|
713
710
|
self.storage.update_container_object(
|
|
714
711
|
element_type, element_item_key, element_item_status
|
|
715
712
|
)
|
|
@@ -746,13 +743,15 @@ class Paradox:
|
|
|
746
743
|
def _is_time_sync_required(self, now) -> bool:
|
|
747
744
|
assert now.tzinfo is None
|
|
748
745
|
try:
|
|
749
|
-
|
|
746
|
+
drift = (
|
|
747
|
+
now - self.storage.get_container("system")["date"]["time"]
|
|
748
|
+
).total_seconds()
|
|
750
749
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
750
|
+
if abs(drift) > cfg.SYNC_TIME_MIN_DRIFT:
|
|
751
|
+
logger.info(f"Time drifted more than allowed: {drift} seconds")
|
|
752
|
+
return True
|
|
753
|
+
else:
|
|
754
|
+
logger.debug(f"Time drifted within allowed range: {drift} seconds")
|
|
756
755
|
|
|
757
756
|
except KeyError:
|
|
758
757
|
pass
|
|
@@ -795,7 +794,7 @@ class Paradox:
|
|
|
795
794
|
|
|
796
795
|
def _on_event(self, event: Event):
|
|
797
796
|
if cfg.LOGGING_DUMP_EVENTS:
|
|
798
|
-
logger.debug("LiveEvent: {}"
|
|
797
|
+
logger.debug(f"LiveEvent: {event}")
|
|
799
798
|
|
|
800
799
|
event.call_hook(storage=self.storage, alarm=self)
|
|
801
800
|
|
|
@@ -813,7 +812,7 @@ class Paradox:
|
|
|
813
812
|
label_provider=self.get_label,
|
|
814
813
|
)
|
|
815
814
|
if cfg.LOGGING_DUMP_EVENTS:
|
|
816
|
-
logger.debug("ChangeEvent: {}"
|
|
815
|
+
logger.debug(f"ChangeEvent: {event}")
|
|
817
816
|
ps.sendEvent(event)
|
|
818
817
|
except AssertionError:
|
|
819
818
|
logger.debug("Could not create event from change")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: paradox-alarm-interface
|
|
3
|
-
Version: 3.4.
|
|
3
|
+
Version: 3.4.2.dev0
|
|
4
4
|
Summary: Interface to Paradox Alarm Panels
|
|
5
5
|
Home-page: https://github.com/ParadoxAlarmInterface/pai
|
|
6
6
|
Author: Jevgeni Kiski, João Paulo Barraca
|
|
@@ -18,14 +18,14 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
-
Requires-Python:
|
|
21
|
+
Requires-Python: <3.12,>=3.8
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: construct~=2.9.43
|
|
25
25
|
Requires-Dist: argparse>=1.4.0
|
|
26
26
|
Requires-Dist: python-slugify>=4.0.1
|
|
27
27
|
Requires-Dist: pytz>=2021.3
|
|
28
|
-
Requires-Dist: paho_mqtt
|
|
28
|
+
Requires-Dist: paho_mqtt<2,>=1.5.0
|
|
29
29
|
Requires-Dist: requests>=2.20.0
|
|
30
30
|
Requires-Dist: pyserial-asyncio>=0.4
|
|
31
31
|
Provides-Extra: yaml
|
|
@@ -107,8 +107,8 @@ See [wiki](https://github.com/ParadoxAlarmInterface/pai/wiki/Installation)
|
|
|
107
107
|
## Tested Environment
|
|
108
108
|
|
|
109
109
|
Tested in the following environment:
|
|
110
|
-
* Python 3.6, 3.7, 3.8
|
|
111
|
-
* Mosquitto MQTT Broker >
|
|
110
|
+
* Python 3.6, 3.7, 3.8, 3.9, 3.10, 3.11
|
|
111
|
+
* Mosquitto MQTT Broker > 1.4.8
|
|
112
112
|
* OrangePi 2G-IOT, NanoPi NEO, and Raspberry Pi 3 through their built in Serial Port (with a level shifter!), or a USB RS232 TTL adapter (CP2102, PL2303, CH340, etc..)
|
|
113
113
|
* Ubuntu Server 16.04.3 LTS
|
|
114
114
|
* Paradox MG5050, SP7000 and EVO panels
|
|
@@ -26,13 +26,13 @@ license = EPL
|
|
|
26
26
|
[options]
|
|
27
27
|
zip_safe = True
|
|
28
28
|
packages = find:
|
|
29
|
-
python_requires = >=3.8
|
|
29
|
+
python_requires = >=3.8,<3.12
|
|
30
30
|
install_requires =
|
|
31
31
|
construct~=2.9.43
|
|
32
32
|
argparse>=1.4.0
|
|
33
33
|
python-slugify>=4.0.1
|
|
34
34
|
pytz>=2021.3
|
|
35
|
-
paho_mqtt>=1.5.0
|
|
35
|
+
paho_mqtt>=1.5.0,<2
|
|
36
36
|
requests>=2.20.0
|
|
37
37
|
pyserial-asyncio>=0.4
|
|
38
38
|
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/connections/handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/data/memory_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/__init__.py
RENAMED
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/event.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/panel.py
RENAMED
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/evo/parsers.py
RENAMED
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/panel.py
RENAMED
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/hardware/parsers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/mqtt/core.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/core.py
RENAMED
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/interfaces/text/gsm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/lib/event_filter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/parsers/__init__.py
RENAMED
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/paradox/parsers/status.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{paradox-alarm-interface-3.4.0 → paradox_alarm_interface-3.4.2.dev0}/tests/test_async_queue.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|