Closed mosterme1003 closed 3 years ago
I think I got it to work for me. Is this a clean way to do it?
from bacpypes.core import deferred
from bacpypes.pdu import Address
from BAC0.core.io.Read import find_reason
from BAC0.core.io.IOExceptions import NoResponseFromController, WritePropertyException
from bacpypes.apdu import WritePropertyRequest, SimpleAckPDU
from bacpypes.iocb import IOCB
from bacpypes.constructeddata import ArrayOf, Any
from bacpypes.basetypes import CalendarEntry
import BAC0
class Calendar:
DateList = ArrayOf(CalendarEntry)
def build_write_request(self, deviceIp, calendarInstance, dateList):
request = WritePropertyRequest(
objectIdentifier=("calendar", calendarInstance),
propertyIdentifier="dateList"
)
request.pduDestination = Address(deviceIp)
_value = Any()
_value.cast_in(dateList)
request.propertyValue = _value
return request
def write_value(self, bacnet, request, vendor_id=0, timeout=10):
try:
iocb = IOCB(request)
iocb.set_timeout(timeout)
# pass to the BACnet stack
deferred(bacnet.this_application.request_io, iocb)
iocb.wait() # Wait for BACnet response
if iocb.ioResponse: # successful response
apdu = iocb.ioResponse
if not isinstance(apdu, SimpleAckPDU): # expect an ACK
print("Not an ack, see debug for more infos.")
return
if iocb.ioError: # unsuccessful: error/reject/abort
apdu = iocb.ioError
reason = find_reason(apdu)
raise NoResponseFromController(
"APDU Abort Reason : {}".format(reason))
except WritePropertyException as error:
# construction error
print(("exception: {!r}".format(error)))
def write_calendar(self, localIp, deviceIp, calendarInstance, dateList):
bacnet = BAC0.connect(ip=localIp)
request = self.build_write_request(deviceIp=deviceIp, calendarInstance=calendarInstance, dateList=dateList)
self.write_value(bacnet, request)
if __name__ == '__main__':
dt1 = (121, 2, 19, 255)
dt2 = (121, 2, 20, 255)
listEntries = [CalendarEntry(date=dt1), CalendarEntry(date=dt2)]
dateList = Calendar.DateList(listEntries)
Cal = Calendar()
Cal.write_calendar(localIp="192.168.178.2", deviceIp="192.168.178.3", calendarInstance=10104, dateList=dateList)
Now that the basic mechanism works, I'd like to think about the way BAC0 should represent the calendar, preferably with something like a dict, like schedules. So it's easy to read or write.
I'm curious also to see if we could use the calendar
python package(https://github.com/python/cpython/blob/3.9/Lib/calendar.py)
Would be cool to have access to a calendar view...
I didn't look into calendar.py
yet but I thought about a way to represent the calendar object's datelist as a dict. I also wrote the write_calendar_dateList()
and read_calendar_dateList()
functions that could be implemented in BAC0. Currently supported are (non-)recurring dates and date ranges but not yet the weeknday option of the calendar entry. What do you think?
from bacpypes.core import deferred
from bacpypes.pdu import Address
from BAC0.core.io.Read import find_reason
from BAC0.core.io.IOExceptions import NoResponseFromController, WritePropertyException
from bacpypes.apdu import WritePropertyRequest, SimpleAckPDU
from bacpypes.iocb import IOCB
from bacpypes.constructeddata import ArrayOf, Any
from bacpypes.basetypes import CalendarEntry, DateRange
import BAC0
import datetime
class Calendar:
"""
Everything you need to write dates and date ranges to a calendar object.
"""
DateList = ArrayOf(CalendarEntry)
datelist_example = {
"dates": [
{
"date": "2021/3/14",
"recurring": False,
},
{
"date": "2021/3/10",
"recurring": True,
},
],
"dateRanges": [
{
"startDate": "2021/3/16",
"endDate": "2021/3/21",
},
{
"startDate": "2021/3/5",
"endDate": "2021/3/7",
},
]
}
def build_write_request(self, deviceIp, calendar_instance, dateList):
request = WritePropertyRequest(
objectIdentifier=("calendar", calendar_instance),
propertyIdentifier="dateList"
)
request.pduDestination = Address(deviceIp)
_value = Any()
_value.cast_in(dateList)
request.propertyValue = _value
return request
def write_value(self, bacnet, request, vendor_id=0, timeout=10):
try:
iocb = IOCB(request)
iocb.set_timeout(timeout)
# pass to the BACnet stack
deferred(bacnet.this_application.request_io, iocb)
iocb.wait() # Wait for BACnet response
if iocb.ioResponse: # successful response
apdu = iocb.ioResponse
if not isinstance(apdu, SimpleAckPDU): # expect an ACK
print("Not an ack, see debug for more infos.")
return
if iocb.ioError: # unsuccessful: error/reject/abort
apdu = iocb.ioError
reason = find_reason(apdu)
raise NoResponseFromController(
"APDU Abort Reason : {}".format(reason))
except WritePropertyException as error:
# construction error
print(("exception: {!r}".format(error)))
def write_calendar_dateList(self, bacnet, deviceIp, calendar_instance, dateList_dict):
"""
This function will create a dateList from the given dict and write it to
the given calendar object.
"""
try:
dateList = self.encode_dateList(dateList_dict)
request = self.build_write_request(deviceIp=deviceIp, calendar_instance=calendar_instance, dateList=dateList)
self.write_value(bacnet, request)
except Exception as error:
print(("exception: {!r}".format(error)))
def read_calendar_dateList(self, bacnet, deviceIp, calendar_instance):
"""
This function will read the dateList property of given calendar object and
pass it to decode_dateList() to convert it into a human readable dict.
"""
try:
dateList_object = bacnet.read("{} calendar {} dateList".format(deviceIp, calendar_instance))
dateList_dict = self.decode_dateList(dateList_object)
except Exception as error:
print(("exception: {!r}".format(error)))
return {}
return dateList_dict
def decode_dateList(self, dateList_object):
dateList_dict = {"dates": [], "dateRanges": []}
for entry in dateList_object:
entry_dict = {}
if entry.date:
if entry.date[3] == 255:
recurring = True
else:
recurring = False
entry_dict["date"] = "{}/{}/{}".format(entry.date[0] + 1900, entry.date[1], entry.date[2])
entry_dict["recurring"] = recurring
dateList_dict["dates"].append(entry_dict)
elif entry.dateRange:
entry_dict["startDate"] = "{}/{}/{}".format(entry.dateRange.startDate[0] + 1900,
entry.dateRange.startDate[1],
entry.dateRange.startDate[2])
entry_dict["endDate"] = "{}/{}/{}".format(entry.dateRange.endDate[0] + 1900,
entry.dateRange.endDate[1],
entry.dateRange.endDate[2])
dateList_dict["dateRanges"].append(entry_dict)
return dateList_dict
def encode_dateList(self, dateList_dict):
"""
From a structured dict (see dateList_example), create a DateList
an ArrayOf(CalendarEntry)
"""
entries = []
if "dates" in dateList_dict.keys():
for date_entry in dateList_dict["dates"]:
year, month, day = (int(x) for x in date_entry["date"].split('/'))
if date_entry["recurring"]:
weekday = 255
else:
weekday = datetime.date(year, month, day).weekday() + 1
if weekday > 7: weekday = 1
_date = (year - 1900, month, day, weekday)
entries.append(CalendarEntry(date=_date))
if "dateRanges" in dateList_dict.keys():
for date_range_entry in dateList_dict["dateRanges"]:
year, month, day = (int(x) for x in date_range_entry["startDate"].split('/'))
weekday = datetime.date(year, month, day).weekday() + 1
if weekday > 7: weekday = 1
start_date = (year - 1900, month, day, weekday)
year, month, day = (int(x) for x in date_range_entry["endDate"].split('/'))
weekday = datetime.date(year, month, day).weekday() + 1
if weekday > 7: weekday = 1
end_date = (year - 1900, month, day, weekday)
date_range = DateRange(startDate=start_date, endDate=end_date)
entries.append(CalendarEntry(dateRange=date_range))
dateList = self.DateList(entries)
return dateList
This issue had no activity for a long period of time. If this issue is still required, please update the status or else, it will be closed. Please note that an issue can be reopened if required.
I've been on other things lately. Sorry. I'll find time to look at this.
I'm actually adding this almost as-is Thanks
This issue had no activity for a long period of time. If this issue is still required, please update the status or else, it will be closed. Please note that an issue can be reopened if required.
Hey! I would like to add entries to a calendar's property
dateList
. Now I have a list with multiplebacpypes.basetypes.CalendarEntry
. How can I write it todateList
? I tried to figure out how you did similar things in theSchedule.py
but I don't really get how this whole request building and sending processes work, especially when I'm sending a list or an array. Can you help me? Is there an easy way for me to write dates to a calendar?