Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timezone & trouble notification fixes #314

Merged
merged 10 commits into from
Sep 5, 2022
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,6 @@ ENV/
/config/*.yaml
/eeprom.bin
/ram.bin

# OS X
.DS_Store
9 changes: 5 additions & 4 deletions config/pai.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import logging
# OUTPUT_PULSE_DURATION = 1 # Duration of a PGM pulse in seconds
# SYNC_TIME = False # Update panel time periodically when time drifts more than SYNC_TIME_MIN_DRIFT
# SYNC_TIME_MIN_DRIFT = 60 # Minimum time drift in seconds to initiate time sync
# SYNC_TIME_TIMEZONE='' # Timezone used for panel time synchronisation. PAI host timezone is used by default
# PASSWORD = '0000' # PC Password. Set to None if Panel has no Password.
# # In Babyware: Right click on your panel -> Properties -> PC Communication (Babyware) ->
# # PC Communication (Babyware) Tab.
Expand Down Expand Up @@ -149,7 +150,7 @@ import logging
## Event filtering by tags:
# HOMEASSISTANT_NOTIFICATIONS_EVENT_FILTERS = [ # list of tags to include or exclude see hardware event.py for tag list
# 'live,alarm,-restore', # or
# 'live,trouble,-clock', # or
# 'trouble,-clock', # or
# 'live,tamper'
# ]
## - OR - event filtering using regexp. Cannot be used together with _EVENT_FILTERS
Expand All @@ -167,7 +168,7 @@ import logging
## Event filtering by tags:
# PUSHBULLET_EVENT_FILTERS = [ # list of tags to include or exclude see hardware event.py for tag list
# 'live,alarm,-restore', # or
# 'live,trouble,-clock', # or
# 'trouble,-clock', # or
# 'live,tamper'
# ]
## - OR - event filtering using regexp. Cannot be used together with _EVENT_FILTERS
Expand All @@ -189,7 +190,7 @@ import logging
## Event filtering by tags:
# PUSHOVER_EVENT_FILTERS = [ # list of tags to include or exclude see hardware event.py for tag list
# 'live,alarm,-restore', # or
# 'live,trouble,-clock', # or
# 'trouble,-clock', # or
# 'live,tamper'
# ]
## - OR - event filtering using regexp. Cannot be used together with _EVENT_FILTERS
Expand All @@ -208,7 +209,7 @@ import logging
## Event filtering by tags:
# SIGNAL_EVENT_FILTERS = [ # list of tags to include or exclude see hardware event.py for tag list
# 'live,alarm,-restore', # or
# 'live,trouble,-clock', # or
# 'trouble,-clock', # or
# 'live,tamper'
# ]
## - OR - event filtering using regexp. Cannot be used together with _EVENT_FILTERS
Expand Down
2 changes: 1 addition & 1 deletion paradox/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "3.0.1"
VERSION = "3.1.0"
13 changes: 7 additions & 6 deletions paradox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ class Config(object):
"SYNC_TIME_MIN_DRIFT": (
120,
int,
(60, 0xFFFFFFFF)
(120, 0xFFFFFFFF)
), # Minimum time drift in seconds to initiate time sync
"SYNC_TIME_TIMEZONE": "", # By default pai uses the same timezone as pai host
"PASSWORD": (
None,
[int, str, bytes, type(None)],
Expand Down Expand Up @@ -182,7 +183,7 @@ class Config(object):
"HOMEASSISTANT_NOTIFICATIONS_ALLOW_EVENTS": [], # Same as before but as a white list. Default is use EVENT_FILTERS
"HOMEASSISTANT_NOTIFICATIONS_EVENT_FILTERS": [ # list of tags, property changes to include or exclude. See event.py for tag list
"live,alarm,-restore",
"live,trouble,-clock",
"trouble,-clock",
"live,tamper",
],
# Pushbullet
Expand All @@ -194,7 +195,7 @@ class Config(object):
"PUSHBULLET_ALLOW_EVENTS": [], # Same as before but as a white list. Default is use EVENT_FILTERS
"PUSHBULLET_EVENT_FILTERS": [ # list of tags, property changes to include or exclude. See event.py for tag list
"live,alarm,-restore",
"live,trouble,-clock",
"trouble,-clock",
"live,tamper",
],
"PUSHBULLET_MIN_EVENT_LEVEL": (
Expand All @@ -212,7 +213,7 @@ class Config(object):
"PUSHOVER_ALLOW_EVENTS": [], # Same as before but as a white list. Default is use EVENT_FILTERS
"PUSHOVER_EVENT_FILTERS": [ # list of tags, property changes to include or exclude. See event.py for tag list
"live,alarm,-restore",
"live,trouble,-clock",
"trouble,-clock",
"live,tamper",
],
"PUSHOVER_MIN_EVENT_LEVEL": (
Expand All @@ -227,7 +228,7 @@ class Config(object):
"SIGNAL_ALLOW_EVENTS": [], # Same as before but as a white list. Default is use EVENT_FILTERS
"SIGNAL_EVENT_FILTERS": [ # list of tags, property changes to include or exclude. See event.py for tag list
"live,alarm,-restore",
"live,trouble,-clock",
"trouble,-clock",
"live,tamper",
],
"SIGNAL_MIN_EVENT_LEVEL": (
Expand Down Expand Up @@ -456,4 +457,4 @@ def get_limits_for_type(elem_type: str, default: list = None):
if isinstance(limits, str):
if "auto" in limits:
return default
return string_to_id_list(limits)
return string_to_id_list(limits)
4 changes: 4 additions & 0 deletions paradox/console_scripts/pai_dump_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from paradox.config import config as cfg
from paradox.lib import help
from paradox.lib.encodings import register_encodings
from paradox.paradox import Paradox

if sys.version_info < (3, 6,):
Expand Down Expand Up @@ -74,6 +75,9 @@ def main():
else:
cfg.load()

# Registering additional encodings
register_encodings()

loop = asyncio.get_event_loop()
loop.run_until_complete(dump_memory(args.file, args.type))

Expand Down
4 changes: 2 additions & 2 deletions paradox/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_format(level):
return "%(asctime)s - %(levelname)-8s - %(name)s - %(message)s"


def config_logger(logger):
def configure_logger(logger):
logger_level = cfg.LOGGING_LEVEL_CONSOLE

if cfg.LOGGING_FILE:
Expand Down Expand Up @@ -119,7 +119,7 @@ def main(args):
else:
cfg.load()

config_logger(logger)
configure_logger(logger)

logger.info(f"Starting Paradox Alarm Interface {VERSION}")
logger.info(f"Config loaded from {cfg.CONFIG_FILE_LOCATION}")
Expand Down
51 changes: 31 additions & 20 deletions paradox/paradox.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import asyncio
import logging
import time
import pytz
from binascii import hexlify
from datetime import datetime
from typing import Callable, Iterable, Optional, Sequence
Expand Down Expand Up @@ -244,18 +245,28 @@ async def dump_memory(self, file, memory_type):
)

async def sync_time(self):
logger.debug("Synchronizing panel time")
now = datetime.now().astimezone()
if cfg.SYNC_TIME_TIMEZONE:
try:
tzinfo = pytz.timezone(cfg.SYNC_TIME_TIMEZONE)
now = now.astimezone(tzinfo)
except pytz.exceptions.UnknownTimeZoneError:
logger.debug(f"Panel Timezone Unknown ('{cfg.SYNC_TIME_TIMEZONE}'). Skipping sync")
return

if not self._is_time_sync_required(now.replace(tzinfo=None)):
return

now = time.localtime()
args = dict(
century=int(now.tm_year / 100),
year=int(now.tm_year % 100),
month=now.tm_mon,
day=now.tm_mday,
hour=now.tm_hour,
minute=now.tm_min,
century=int(now.year / 100),
year=int(now.year % 100),
month=now.month,
day=now.day,
hour=now.hour,
minute=now.minute,
)

logger.debug("Synchronizing panel time")
reply = await self.send_wait(
self.panel.get_message("SetTimeDate"), args, reply_expected=0x03, timeout=10
)
Expand Down Expand Up @@ -713,8 +724,7 @@ def _on_status_update(self, status):
self._update_partition_states()

if cfg.SYNC_TIME:
if self._check_if_time_sync_required():
self.work_loop.create_task(self.sync_time())
self.work_loop.create_task(self.sync_time())

def _process_trouble_statuses(self, trouble_statuses):
global_trouble = False
Expand All @@ -733,20 +743,21 @@ def _process_trouble_statuses(self, trouble_statuses):
"system", "troubles", {"trouble": global_trouble}
)

def _check_if_time_sync_required(self):
def _is_time_sync_required(self, now) -> bool:
assert now.tzinfo is None
try:
drift = (
datetime.now() - self.storage.get_container("system")["date"]["time"]
).total_seconds()
drift = (now - self.storage.get_container("system")["date"]["time"]).total_seconds()

if abs(drift) > cfg.SYNC_TIME_MIN_DRIFT:
logger.info(f"Time drifted more than allowed: {drift} seconds")
return True
else:
logger.debug(f"Time drifted within allowed range: {drift} seconds")
if abs(drift) > cfg.SYNC_TIME_MIN_DRIFT:
logger.info(f"Time drifted more than allowed: {drift} seconds")
return True
else:
logger.debug(f"Time drifted within allowed range: {drift} seconds")

except KeyError:
return False
pass

return False

def _update_partition_states(self):
"""
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ require-python-3
construct~=2.9.43
argparse>=1.4.0
python-slugify>=4.0.1
pytz>=2021.3
#
# Optional
#
Expand Down