2 * clock - clock resource for zavai
4 * Copyright (C) 2009--2010 Enrico Zini <enrico@enricozini.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 public enum SourceType
33 TODO: schedule alarms via at
37 atq -q z can be used to list the jobs (for example, at startup, or after a job has run)
38 at -c id can be used to query a job, parsing its contents (which can have
39 comments or variables being set)
40 zavai --notify ... can be used to notify the job (and start zavai if it's not running)
42 Alarm needs to be able to serialize itself to an at invocation and to
43 deserialize itself from the output of at -c
45 Alarm needs to deserialize also a job with no special markers whatsoever: a
52 oldtime = next_alarm ? next_alarm.time : 0
53 next_alarm = the first alarm from atq
54 if (oldtime != next_alarm.time)
56 remove existing triggers
57 (triggers can be skipped if we don't need to support non-zavai alarms)
58 schedule a trigger calling refresh_alarms() at next_alarm.time + 30 seconds
59 (triggers can be skipped if we don't need to support non-zavai alarms)
63 at clock constructor: refresh_alarms()
64 inotifywait -e close /usr/bin/at -> refresh_alarms() (shows when someone has just used at)
65 (can be skipped if we don't need to support non-zavai alarms)
66 at alarm triggered through zavai: refresh_alarms()
72 public class Alarm : Object
78 public static void schedule(string timespec, string label) throws Error
81 argv[0] = "/usr/bin/at";
90 if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null))
94 FileStream fs = FileStream.fdopen(stdinfd, "w");
95 string display = GLib.Environment.get_variable("DISPLAY");
97 fs.printf("DISPLAY=\"%s\"; export DISPLAY\n", display);
98 fs.printf("# Zavai variables start here\n");
99 fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape(""));
100 fs.printf("# Zavai commands starts here\n");
101 fs.printf("%s notify \"$ZAVAI_LABEL\"", zavai.config.argv0);
104 Process.close_pid(pid);
107 // Get the label of the job with the given at ID
108 public static string? getLabel(int atID)
111 at.jobContents(atID, fd => {
112 FileStream fs = FileStream.fdopen(fd, "r");
115 string? line = fs.read_line();
116 if (line == null) break;
117 if (line.has_prefix("ZAVAI_LABEL=\""))
119 size_t size = line.size();
120 if (size < 15) continue;
121 label = line.substring(13, (long)(size - 14));
122 label = label.compress();
132 [DBus (name = "org.enricozini.zavai.Alarm")]
133 public class ZavaiClock : Object {
134 public void Notify (string label) {
135 clock.notify_alarm(label);
139 public class AlarmTriggerInfo
144 public bool canceled;
146 public AlarmTriggerInfo(string label)
155 public class AlarmTriggerQueue : zavai.Service
157 protected List<AlarmTriggerInfo> queue;
159 public signal void triggered(AlarmTriggerInfo info);
160 public signal void acked(AlarmTriggerInfo info);
161 public signal void canceled(AlarmTriggerInfo info);
163 public AlarmTriggerQueue()
165 queue = new List<AlarmTriggerInfo>();
168 public uint enqueue_trigger(AlarmTriggerInfo info)
170 // Reuse IDs from the associated logger object
171 info.id = zavai.log.log.start("alarm", "Alarm " + info.label);
173 if (queue.data.id == info.id)
174 triggered(queue.data);
178 protected void done_with_first()
180 var first = queue.data;
181 queue.remove_link(queue);
183 triggered(queue.data);
186 public void ack(AlarmTriggerInfo info)
188 if (queue == null || info.id != queue.data.id) return;
189 if (!info.acked && !info.canceled)
193 zavai.log.log.add(info.id, "alarm acknowledged");
194 zavai.log.log.end(info.id);
199 public void cancel(AlarmTriggerInfo info)
201 if (queue == null || info.id != queue.data.id) return;
202 if (!info.acked && !info.canceled)
204 info.canceled = true;
206 zavai.log.log.add(info.id, "alarm canceled");
207 zavai.log.log.end(info.id);
213 public class Clock: zavai.Service
215 protected time_t last_gps_time;
216 protected time_t last_gps_time_system_time;
217 protected time_t last_system_time;
218 protected uint system_time_timeout;
219 protected time_t last_minute;
220 protected time_t chosen_time;
221 protected SourceType chosen_type;
222 protected ZavaiClock dbusClock;
224 protected dynamic DBus.Object otimed_alarm;
225 protected dynamic DBus.Object rtc;
226 protected SList<Alarm> alarms;
228 // Ticks once a minute
229 public signal void minute_changed(long time, SourceType source);
230 public signal void schedule_changed(Alarm? next);
234 Object(name: "clock");
236 dbusClock = new ZavaiClock();
239 last_gps_time_system_time = 0;
240 last_system_time = time_t();
241 chosen_time = last_system_time;
244 otimed_alarm = zavai.registry.sbus.get_object(
245 "org.freesmartphone.otimed",
246 "/org/freesmartphone/Time/Alarm",
247 "org.freesmartphone.Time.Alarm");
249 rtc = zavai.registry.sbus.get_object(
250 "org.freesmartphone.odeviced",
251 "/org/freesmartphone/Device/RTC/0",
252 "org.freesmartphone.Device.RealtimeClock");
254 zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
257 public void notify_alarm(string label)
259 stderr.printf("Notifying %s\n", label);
260 AlarmTriggerInfo info = new AlarmTriggerInfo(label);
261 alarm_trigger_queue.enqueue_trigger(info);
262 schedule_changed(next_alarm());
265 public Alarm? next_alarm()
268 ev = at.earliestID("z");
269 if (ev.deadline == 0)
271 string label = Alarm.getLabel(ev.id);
272 Alarm res = new Alarm();
278 public void schedule(string timespec, string label) throws Error
280 Alarm.schedule(timespec, label);
281 schedule_changed(next_alarm());
284 private void on_gps_time(uint t)
288 last_gps_time_system_time = 0;
291 last_gps_time = (time_t)t;
292 last_gps_time_system_time = time_t();
297 private bool on_system_time()
299 last_system_time = time_t();
304 private void update_time()
306 if (last_gps_time_system_time + 10 > last_system_time)
308 chosen_time = last_gps_time;
309 chosen_type = SourceType.GPS;
313 chosen_time = last_system_time;
314 chosen_type = SourceType.SYSTEM;
316 if (chosen_time / 60 != last_minute)
318 last_minute = chosen_time / 60;
319 minute_changed(chosen_time, chosen_type);
323 /// Request GPS resource
324 public override void start()
328 system_time_timeout = Timeout.add(5000, on_system_time);
329 zavai.gps.gps.time_changed += on_gps_time;
330 last_system_time = time_t();
336 public override void stop()
338 if (!started) return;
340 Source.remove(system_time_timeout);
341 zavai.gps.gps.time_changed -= on_gps_time;
347 public Clock clock = null;
348 public AlarmTriggerQueue alarm_trigger_queue = null;
353 alarm_trigger_queue = new AlarmTriggerQueue();