client: save and load alarms from hardcoded file
[fuzzy_alarm_clock.git] / client / ur_fuzzy_alarm_clock.py
blob5364e523808e5b60fd96cbd4148e68cc55646e8e
1 #!/usr/bin/env python3
3 import datetime
4 import io
5 import json
6 import logging
7 import sys
9 try:
10 import urwid
11 except ImportError:
12 logging.error('This program needs urwid')
13 sys.exit(1)
15 try:
16 import serial
17 except ImportError:
18 logging.error('This program needs pySerial')
19 sys.exit(1)
21 class FuzzyAlarmClock:
23 def __init__(self,device):
24 try:
25 self.serial = serial.Serial(device,9600,timeout=5)
26 except serial.serialutil.SerialException:
27 logging.warning('No such serial device {device}; mocking'.format(
28 device=device
30 self.serial = io.StringIO()
31 self.mock = True
32 else:
33 self.mock = False
35 def set_time_to_now(self):
36 now = 's'+datetime.datetime.now().strftime("%y%m%d%u%H%M%S")
37 self.serial.write(now)
39 def read_alarms(self,n):
40 data = []
41 self.serial.flushInput()
42 self.serial.write('p')
43 for i in range(n):
44 data.append(self.serial.readline())
45 for i in range(n):
46 data[i] = data[i].split(' ')[2:-1]
47 days = []
48 for j in range(8):
49 days.append(bool(int(data[i][0]) & 2**j))
50 days = days[7:8] + days[:7]
51 data[i] = days+data[i][1:]
52 return data
54 def set_alarm(self,alarm,values):
55 s = 'a'+str(alarm)
56 b0 = 0
57 for i,bit in enumerate(values[1:8] + values[0:1]):
58 b0 += bit << i
59 s += "%02x"%(b0)
60 for val in values[8:]:
61 s += "%02x"%(val)
62 self.serial.write(s)
64 class ModEdit(urwid.IntEdit):
66 def __init__(self,mod,caption="",default=None,offset=0):
67 self.mod = mod
68 self.offset = offset
70 super(ModEdit,self).__init__(caption,default)
72 def insert_text(self,text):
73 super(ModEdit,self).insert_text(text)
74 self.set_edit_text(str((self.value() - self.offset)% self.mod+self.offset))
77 class Alarm:
79 def __init__(self,aid,fac):
80 self.w_aid = urwid.Text(str(aid))
81 self.w_repeat = urwid.CheckBox("")
82 self.w_mon = urwid.CheckBox("")
83 self.w_tue = urwid.CheckBox("")
84 self.w_wed = urwid.CheckBox("")
85 self.w_thu = urwid.CheckBox("")
86 self.w_fri = urwid.CheckBox("")
87 self.w_sat = urwid.CheckBox("")
88 self.w_sun = urwid.CheckBox("")
89 self.w_month = ModEdit(12,offset=1)
90 self.w_day = ModEdit(31,offset=1)
91 self.w_hour = ModEdit(24,default=0)
92 self.w_minute = ModEdit(60,default=0)
94 self.widgets = urwid.Columns([self.w_aid,
95 self.w_repeat,
96 self.w_mon,
97 self.w_tue,
98 self.w_wed,
99 self.w_thu,
100 self.w_fri,
101 self.w_sat,
102 self.w_sun,
103 self.w_month,
104 self.w_day,
105 self.w_hour,
106 self.w_minute,
108 dividechars=1)
110 def set_values(self,values):
111 self.w_repeat.set_state(values[0])
112 self.w_mon.set_state(values[1])
113 self.w_tue.set_state(values[2])
114 self.w_wed.set_state(values[3])
115 self.w_thu.set_state(values[4])
116 self.w_fri.set_state(values[5])
117 self.w_sat.set_state(values[6])
118 self.w_sun.set_state(values[7])
119 self.w_month.set_edit_text(str(values[8]))
120 self.w_day.set_edit_text(str(values[9]))
121 self.w_hour.set_edit_text(str(values[10]))
122 self.w_minute.set_edit_text(str(values[11]))
124 def get_values(self):
125 return [
126 self.w_repeat.get_state(),
127 self.w_mon.get_state(),
128 self.w_tue.get_state(),
129 self.w_wed.get_state(),
130 self.w_thu.get_state(),
131 self.w_fri.get_state(),
132 self.w_sat.get_state(),
133 self.w_sun.get_state(),
134 self.w_month.value(),
135 self.w_day.value(),
136 self.w_hour.value(),
137 self.w_minute.value(),
141 class UI:
143 def __init__(self,fac):
144 self.fac = fac
146 self.alarm_number=4
148 self.frame = self.build_ui()
149 self.loop = urwid.MainLoop(self.frame,self.palette,unhandled_input=self.read_commands)
151 def build_ui(self):
152 self.palette = [
153 ('header', 'light gray', 'dark blue'),
154 ('footer', 'dark cyan', 'black')
157 self.alarms = []
158 for i in range(self.alarm_number):
159 self.alarms.append(Alarm(i,self.fac))
161 alarm_headers = urwid.Columns([
162 urwid.Text('#'),
163 urwid.Text('rep'),
164 urwid.Text('Mon'),
165 urwid.Text('Tue'),
166 urwid.Text('Wed'),
167 urwid.Text('Thu'),
168 urwid.Text('Fri'),
169 urwid.Text('Sat'),
170 urwid.Text('Sun'),
171 urwid.Text('mm'),
172 urwid.Text('dd'),
173 urwid.Text('HH'),
174 urwid.Text('MM'),
175 ],dividechars=1)
176 self.main_body = urwid.ListBox([alarm_headers]+[al.widgets for al in self.alarms])
178 footer = urwid.Text('a, b, c')
179 footer_map = urwid.AttrMap(footer,'footer')
181 header = urwid.Text(
182 'q: Quit ' +
183 'r: Read s: Set n: time=Now ' +
184 'f: to File l: Load ' +
185 '?:Help'
187 header_map = urwid.AttrMap(header,'header')
189 ui = urwid.Frame(self.main_body,footer=footer_map,header=header_map)
190 return ui
192 def read_commands(self,cmd):
193 {'n': self.fac.set_time_to_now,
194 'r': self.read_alarms,
195 's': self.set_alarms,
196 '?': self.print_help,
197 'q': self.quit,
198 'f': self.save_alarms,
199 'l': self.load_alarms,
200 }.get(cmd,self.do_nothing)()
202 def do_nothing(self):
203 pass
205 def read_alarms(self):
206 alarms = self.fac.read_alarms(self.alarm_number)
208 for i, al in enumerate(alarms):
209 self.alarms[i].set_values(al)
211 def set_alarms(self):
212 for i,alarm in enumerate(self.alarms):
213 self.fac.set_alarm(i,alarm.get_values())
215 def save_alarms(self, fname='saved_alarms.json'):
216 alarms = [a.get_values() for a in self.alarms]
218 with open(fname,'w') as fp:
219 json.dump(alarms, fp)
221 def load_alarms(self, fname='saved_alarms.json'):
222 try:
223 with open(fname) as fp:
224 alarms = json.load(fp)
225 except FileNotFoundError:
226 logging.warning('No such file: {fname}'.format(fname=fname))
227 return False
228 # TODO: do some validation on the data
229 for i, a in enumerate(self.alarms):
230 pass
231 a.set_values(alarms[i])
233 def print_help(self):
234 pass
236 def quit(self):
237 raise urwid.ExitMainLoop()
239 def run(self):
240 self.loop.run()
243 def main():
244 fac = FuzzyAlarmClock('/dev/ttyUSB0')
245 ui = UI(fac)
246 ui.run()
248 if __name__ == '__main__': main()