Files
arduino-timex-datalink-pytimex/pytimex/TimexData.py
T
Simon Larsson 081b4d73a7 Fix phone numbers, charset and DATA1 splits
* Phone numbers are not stored as strings anymore
* Charset has some new symbols (Please find better substitution characters)
* Number of DATA1 packets must be in START2 header - fixed!
2020-07-05 09:12:07 +02:00

248 lines
6.8 KiB
Python

import datetime
from ._helpers import *
monthNamesAbbr = [
"<unknown>", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
]
class TimexAppointment:
def __init__(self, month=0, day=0, time=0, label=""):
self.month=month
self.day=day
self.time=time
self.label=label
def __str__(self):
ctime = "{:02}:{:02}".format(int(self.time/4), (self.time&0x03)*15)
return "Appointment on the {} of {} at {}, label \"{}\"".format(
self.day, monthNamesAbbr[self.month], ctime, self.label)
def __bytes__(self):
data = [self.month&0xFF, self.day&0xFF, self.time&0xFF]
data = data + pack4to3(str2timex(self.label))
data = [len(data)+1] + data
return bytes(data)
class TimexTodo:
def __init__(self, prio=0, label=""):
self.prio=prio
self.label=label
def __str__(self):
p = "priority {}".format(self.prio) if self.prio else "no priority"
return "Todo with {}, label \"{}\"".format(
p, self.label)
def __bytes__(self):
data = [self.prio]
data = data + pack4to3(str2timex(self.label))
data = [len(data)+1] + data
return bytes(data)
class TimexPhoneNumber:
def __init__(self, number="1", label=""):
self.number=number
self.label=label
def __str__(self):
return "Phone number {}, label \"{}\"".format(
self.number, self.label)
def __bytes__(self):
# TODO: This only works with numbers 10 digits or less, and no type indication
# Convert to digits
conv_table = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'C':10,'F':11,'H':12,'P':13,'W':14,' ':15}
digits = [conv_table[x] for x in str(self.number)]
# Pad with F
t = [15]*12 # Make a "template" filled with F
t[-2-len(digits):-2] = digits # Replace part of template
# Smush it up
data = [d[1]<<4|d[0] for d in zip(t[0::2], t[1::2]) ]
data = data + pack4to3(str2timex(self.label))
data = [len(data)+1] + data
return bytes(data)
class TimexAnniversary:
def __init__(self, month=1, day=1, label=""):
self.month=month
self.day=day
self.label=label
def __str__(self):
return "Anniversary on the {} of {}, label \"{}\"".format(
self.day, monthNamesAbbr[self.month], self.label)
def __bytes__(self):
data = [self.month&0xFF, self.day&0xFF]
data = data + pack4to3(str2timex(self.label))
data = [len(data)+1] + data
return bytes(data)
class TimexTimezone:
def __init__(self, offset=0, format=24, name=""):
self.offset = offset
self._format = format
self.name = name
@property
def format(self):
return self._format
@format.setter
def format(self, f):
if not f in [12,24]:
raise Exception("Time format must be 12 or 24 hours")
self.format = f
def __str__(self):
return "Time zone with offset UTC{:+} named \"{}\", {} hour format".format(offset, name, format)
class TimexAlarm:
def __init__(self, hour=0, minute=0, month=0, day=0, label="", audible=True):
self.hour = hour
self.minute = minute
self.month = month
self.day = day
self.label = label
self.audible = audible
def __str__(self):
audible_str = "audible" if self.audible else "inaudible"
if self.day==0 and self.month==0:
return "Alarm at {:02d}:{:02d}, label \"{}\", {}".format(
self.hour, self.minute, self.label, audible_str)
if self.day==0:
return "Alarm at {:02d}:{:02d} every day in {}, label \"{}\", {}".format(
self.hour, self.minute, monthNamesAbbr[self.month], self.label, audible_str)
if self.month==0:
return "Alarm at {:02d}:{:02d} on the {}, label \"{}\", {}".format(
self.hour, self.minute, self.day, self.label, audible_str)
return "Alarm at {:02d}:{:02d} on the {} of {}, label \"{}\", {}".format(
self.hour, self.minute, self.day, monthNamesAbbr[self.month], self.label, audible_str)
class TimexData:
def __init__(self):
self.appointments = []
self.todos = []
self.phonenumbers = []
self.anniversaries = []
self.alarms = []
self.sendTime = False
self.tz=[
TimexTimezone(0, 24, "utc"),
TimexTimezone(-5, 12, "est")
]
def setTimezone(self, tzno, offset, format, name):
if tzno not in [1,2]:
raise Exception("Time zone number must be 1 or 2!")
# TODO: Move these checks to time zone class
if format not in [12,24]:
raise Exception("Time format must be 12 or 24!")
if len(name)>3:
raise Exception("Max length for time zone name is 3 characters!")
self.tz[tzno-1] = TimexTimezone(offset, format, name)
def addAppointment(self, appointment):
self.appointments.append(appointment)
def addNewAppointment(self, *args, **kwargs):
new = TimexAppointment(*args, **kwargs)
self.addAppointment(new)
return new
def delAppointment(self, appointment):
self.appointments = [a for a in self.appointments if a != appointment]
def addTodo(self, todo):
self.todos.append(todo)
def addNewTodo(self, *args, **kwargs):
new = TimexTodo(*args, **kwargs)
self.addTodo(new)
return new
def delTodo(self, todo):
self.todos = [a for a in self.todos if a != todo]
def addPhoneNumber(self, phonenumber):
self.phonenumbers.append(phonenumber)
def addNewPhoneNumber(self, *args, **kwargs):
new = TimexPhoneNumber(*args, **kwargs)
self.addPhoneNumber(new)
return new
def delPhoneNumber(self, phonenumber):
self.phonenumbers = [a for a in self.phonenumbers if a != phonenumber]
def addAnniversary(self, anniversary):
self.anniversaries.append(anniversary)
def addNewAnniversary(self, *args, **kwargs):
new = TimexAnniversary(*args, **kwargs)
self.addAnniversary(new)
return new
def delAnniversary(self, anniversary):
self.anniversaries = [a for a in self.anniversaries if a != anniversary]
def addAlarm(self, alarm):
self.alarms.append(alarm)
def addNewAlarm(self, *args, **kwargs):
new = TimexAlarm(*args, **kwargs)
self.addAlarm(new)
return new
def delAlarm(self, alarm):
self.alarms = [a for a in self.alarms if a != alarm]
def __bytes__(self):
data = b''
data += bytes(makeSTART1())
if self.sendTime:
now = datetime.datetime.utcnow()
tz1time = now+datetime.timedelta(hours=self.tz[0].offset)
tz2time = now+datetime.timedelta(hours=self.tz[1].offset)
data += bytes(makeTZ(2, tz2time, self.tz[1].format))
data += bytes(makeTZ(1, tz1time, self.tz[0].format))
data += bytes(makeTZNAME(1, self.tz[0].name))
data += bytes(makeTZNAME(2, self.tz[1].name))
if (
len(self.appointments)>0 or
len(self.todos)>0 or
len(self.phonenumbers)>0 or
len(self.anniversaries)>0
):
data += bytes(makeSTART2(DATA1_num_packets(self.appointments, self.todos, self.phonenumbers, self.anniversaries)))
data += bytes(makeDATA1(self.appointments, self.todos, self.phonenumbers, self.anniversaries))
data += bytes(makeEND1())
# I don't know if all five alarms must be sent every time.
# If so, make dummy alarms.
if len(self.alarms)>0:
i=1
for a in self.alarms:
data += makeALARM(a, i)
i+=1
data += bytes(makeEND2())
return data