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
141 public zavai.log.Log log;
144 public bool canceled;
146 public AlarmTriggerInfo(string label)
154 public class AlarmTriggerQueue : zavai.Service
156 protected List<AlarmTriggerInfo> queue;
158 public signal void triggered(AlarmTriggerInfo info);
159 public signal void acked(AlarmTriggerInfo info);
160 public signal void canceled(AlarmTriggerInfo info);
162 public AlarmTriggerQueue()
164 queue = new List<AlarmTriggerInfo>();
167 public void enqueue_trigger(AlarmTriggerInfo info)
169 // Reuse IDs from the associated logger object
170 info.log = zavai.log.log.start("alarm", "Alarm " + info.label);
172 if (queue.data == info)
173 triggered(queue.data);
176 protected void done_with_first()
178 var first = queue.data;
179 queue.remove_link(queue);
181 triggered(queue.data);
184 public void ack(AlarmTriggerInfo info)
186 if (queue == null || info != queue.data) return;
187 if (!info.acked && !info.canceled)
191 info.log.add("alarm acknowledged");
192 info.log.acked = true;
193 zavai.log.log.end(info.log);
198 public void cancel(AlarmTriggerInfo info)
200 if (queue == null || info != queue.data) return;
201 if (!info.acked && !info.canceled)
203 info.canceled = true;
205 info.log.add("alarm canceled");
206 zavai.log.log.end(info.log);
212 public class Clock: zavai.Service
214 protected time_t last_gps_time;
215 protected time_t last_gps_time_system_time;
216 protected time_t last_system_time;
217 protected uint system_time_timeout;
218 protected time_t last_minute;
219 protected time_t chosen_time;
220 protected SourceType chosen_type;
221 protected ZavaiClock dbusClock;
223 protected dynamic DBus.Object otimed_alarm;
224 protected dynamic DBus.Object rtc;
225 protected SList<Alarm> alarms;
227 // Ticks once a minute
228 public signal void minute_changed(long time, SourceType source);
229 public signal void schedule_changed(Alarm? next);
233 Object(name: "clock");
235 dbusClock = new ZavaiClock();
238 last_gps_time_system_time = 0;
239 last_system_time = time_t();
240 chosen_time = last_system_time;
243 otimed_alarm = zavai.registry.sbus.get_object(
244 "org.freesmartphone.otimed",
245 "/org/freesmartphone/Time/Alarm",
246 "org.freesmartphone.Time.Alarm");
248 rtc = zavai.registry.sbus.get_object(
249 "org.freesmartphone.odeviced",
250 "/org/freesmartphone/Device/RTC/0",
251 "org.freesmartphone.Device.RealtimeClock");
253 zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
256 public void notify_alarm(string label)
258 stderr.printf("Notifying %s\n", label);
259 AlarmTriggerInfo info = new AlarmTriggerInfo(label);
260 alarm_trigger_queue.enqueue_trigger(info);
261 schedule_changed(next_alarm());
264 public Alarm? next_alarm()
267 ev = at.earliestID("z");
268 if (ev.deadline == 0)
270 string label = Alarm.getLabel(ev.id);
271 Alarm res = new Alarm();
277 public void schedule(string timespec, string label) throws Error
279 Alarm.schedule(timespec, label);
280 schedule_changed(next_alarm());
283 private void on_gps_time(uint t)
287 last_gps_time_system_time = 0;
290 last_gps_time = (time_t)t;
291 last_gps_time_system_time = time_t();
296 private bool on_system_time()
298 last_system_time = time_t();
303 private void update_time()
305 if (last_gps_time_system_time + 10 > last_system_time)
307 chosen_time = last_gps_time;
308 chosen_type = SourceType.GPS;
312 chosen_time = last_system_time;
313 chosen_type = SourceType.SYSTEM;
315 if (chosen_time / 60 != last_minute)
317 last_minute = chosen_time / 60;
318 minute_changed(chosen_time, chosen_type);
322 /// Request GPS resource
323 public override void start()
327 system_time_timeout = Timeout.add(5000, on_system_time);
328 zavai.gps.gps.time_changed += on_gps_time;
329 last_system_time = time_t();
335 public override void stop()
337 if (!started) return;
339 Source.remove(system_time_timeout);
340 zavai.gps.gps.time_changed -= on_gps_time;
346 public Clock clock = null;
347 public AlarmTriggerQueue alarm_trigger_queue = null;
352 alarm_trigger_queue = new AlarmTriggerQueue();