Source code for BAC0.core.functions.TimeSync
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 by Christian Tremblay, P.Eng <christian.tremblay@servisys.com>
# Licensed under LGPLv3, see file LICENSE in this source tree.
#
"""
TimeSync.py - creation of time synch requests
"""
# --- standard Python modules ---
import datetime as dt
from datetime import datetime
import pytz
from bacpypes.apdu import TimeSynchronizationRequest, UTCTimeSynchronizationRequest
from bacpypes.basetypes import DateTime
from bacpypes.core import deferred
from bacpypes.iocb import IOCB
# --- 3rd party modules ---
from bacpypes.pdu import Address, GlobalBroadcast, LocalBroadcast
from bacpypes.primitivedata import Date, Time
from ...core.utils.notes import note_and_log
from ..io.IOExceptions import (
ApplicationNotStarted,
)
def _build_datetime(UTC=False):
if UTC:
_d = dt.datetime.utcnow().date()
_t = dt.datetime.utcnow().time()
_date = Date(
year=_d.year - 1900, month=_d.month, day=_d.day, day_of_week=_d.isoweekday()
).value
_time = Time(
hour=_t.hour,
minute=_t.minute,
second=_t.second,
hundredth=int(_t.microsecond / 10000),
).value
else:
_date = Date().now().value
_time = Time().now().value
return DateTime(date=_date, time=_time)
[docs]@note_and_log
class TimeSync:
"""
Mixin to support Time Synchronisation from BAC0 to other devices
"""
[docs] def time_sync(self, destination=None, datetime=None, UTC=False):
"""
Take local time and send it to devices. User can also provide
a datetime value (constructed following bacpypes.basetypes.Datetime
format).
To create a DateTime ::
from bacpypes.basetypes import DateTime
from bacpypes.primitivedata import Date, Time
# Create date and time
_date = Date('2019-08-05')
_time = Time('16:45')
# Create Datetime
_datetime = DateTime(date=_date.value, time=_time.value)
# Pass this to the function
bacnet.time_sync(datetime=_datetime)
"""
if not self._started:
raise ApplicationNotStarted("BACnet stack not running - use startApp()")
if not datetime:
_datetime = _build_datetime(UTC=UTC)
elif isinstance(datetime, DateTime):
_datetime = datetime
else:
raise ValueError(
"Please provide valid DateTime in bacpypes.basetypes.DateTime format"
)
# build a request
if UTC:
request = UTCTimeSynchronizationRequest(time=_datetime)
else:
request = TimeSynchronizationRequest(time=_datetime)
if destination:
if destination.lower() == "global":
request.pduDestination = GlobalBroadcast()
elif destination.lower() == "local":
request.pduDestination = LocalBroadcast()
else:
try:
request.pduDestination = Address(destination)
except (TypeError, ValueError):
self._log.warning(
"Destination unrecognized ({}), setting local broadcast".format(
destination
)
)
request.pduDestination = LocalBroadcast()
else:
request.pduDestination = LocalBroadcast()
self._log.debug("{:>12} {}".format("- request:", request))
iocb = IOCB(request) # make an IOCB
# pass to the BACnet stack
deferred(self.this_application.request_io, iocb)
# Unconfirmed request...so wait until complete
iocb.wait() # Wait for BACnet response
year, month, day, dow = _datetime.date
year = year + 1900
hour, minutes, sec, msec = _datetime.time
d = dt.datetime(year, month, day, hour, minutes, sec, msec)
self._log.info("Time Sync Request sent to network : {}".format(d.isoformat()))
[docs]class TimeHandler(object):
"""
This class will deal with Time / Timezone related features
To deal with DateTime Value correctly we need to be aware
of timezone.
"""
def __init__(self, tz: str = "America/Montreal") -> None:
self.set_timezone(tz)
[docs] def set_timezone(self, tz: str) -> None:
self.timezone = pytz.timezone(tz)
@property
def now(self) -> datetime:
return dt.datetime.now()
[docs] def local_time(self):
return self.now.time()
[docs] def local_date(self):
return self.now.date()
[docs] def utcOffset(self) -> float:
"Returns UTC offset in minutes"
return round(self.now.astimezone().utcoffset().total_seconds() / 60) # type: ignore[union-attr]
[docs] def is_dst(self) -> bool:
return self.timezone.dst(self.now) != dt.timedelta(0)
def __repr__(self):
return "{}".format(self.__dict__)