Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 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
5 changes: 3 additions & 2 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 @@ -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)
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