2 * clock - clock resource for zavai
4 * Copyright (C) 2009 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
75 // Notify of an alarm being triggered
76 public signal void trigger(Alarm a);
78 public time_t deadline;
81 public Alarm(time_t deadline, string label)
83 this.deadline = deadline;
91 public static void schedule(string timespec, string label) throws Error
94 argv[0] = "/usr/bin/at";
101 if (!Process.spawn_async_with_pipes("/", argv, null, SpawnFlags.STDERR_TO_DEV_NULL, null, out pid, out stdinfd, null, null))
105 FileStream fs = FileStream.fdopen(stdinfd, "w");
106 fs.printf("# Zavai variables start here\n");
107 fs.printf("ZAVAI_LABEL=\"%s\"\n", label.escape(""));
108 fs.printf("# Zavai commands starts here\n");
109 fs.printf("echo \"$ZAVAI_LABEL\" | zavai --notify");
112 Process.close_pid(pid);
115 // Get the label of the job with the given at ID
116 public static string? getLabel(int atID)
119 at.jobContents(atID, fd => {
120 FileStream fs = FileStream.fdopen(fd, "r");
123 string? line = fs.read_line();
124 if (line == null) break;
125 if (line.has_prefix("ZAVAI_LABEL=\""))
127 size_t size = line.size();
128 if (size < 15) continue;
129 label = line.substring(13, (long)(size - 14));
130 label = label.compress();
140 private int alarm_compare(void* a, void* b)
142 return (int)(((Alarm*)a)->deadline - ((Alarm*)b)->deadline);
145 [DBus (name = "org.freesmartphone.Notification")]
146 public class AlarmNotification : Object {
147 public void Alarm () {
148 clock.check_alarms();
152 public class Clock: zavai.Service
154 protected time_t last_gps_time;
155 protected time_t last_gps_time_system_time;
156 protected time_t last_system_time;
157 protected dynamic DBus.Object gps_time;
158 protected uint system_time_timeout;
159 protected time_t last_minute;
160 protected time_t chosen_time;
161 protected SourceType chosen_type;
162 protected AlarmNotification listener;
164 protected dynamic DBus.Object otimed_alarm;
165 protected dynamic DBus.Object rtc;
166 protected SList<Alarm> alarms;
168 // Ticks once a minute
169 public signal void minute_changed(long time, SourceType source);
170 public signal void schedule_changed();
174 Object(name: "clock");
176 listener = new AlarmNotification();
179 last_gps_time_system_time = 0;
180 last_system_time = time_t();
181 chosen_time = last_system_time;
183 gps_time = zavai.registry.sbus.get_object(
184 "org.freesmartphone.ogpsd",
185 "/org/freedesktop/Gypsy",
186 "org.freedesktop.Gypsy.Time");
189 otimed_alarm = zavai.registry.sbus.get_object(
190 "org.freesmartphone.otimed",
191 "/org/freesmartphone/Time/Alarm",
192 "org.freesmartphone.Time.Alarm");
194 rtc = zavai.registry.sbus.get_object(
195 "org.freesmartphone.odeviced",
196 "/org/freesmartphone/Device/RTC/0",
197 "org.freesmartphone.Device.RealtimeClock");
199 zavai.registry.sbus.register_object("/", listener);
203 public Alarm? next_alarm()
210 public void schedule(Alarm a)
212 alarms.insert_sorted(a, alarm_compare);
216 private void otimed_reschedule()
220 zavai.log.info("Scheduling next alarm: " + alarms.data.label + " at " + Time.local(alarms.data.deadline).to_string());
221 zavai.log.info("Scheduling at abs " + "%d".printf((int)alarms.data.deadline));
224 otimed_alarm.ClearAlarm(zavai.registry.bus_name);
226 zavai.log.error("Cannot clear alarms: " + e.message);
229 otimed_alarm.SetAlarm(zavai.registry.bus_name, (int)alarms.data.deadline);
231 zavai.log.error("Cannot reschedule alarms: " + e.message);
234 int t = rtc.GetCurrentTime();
235 stderr.printf("Current time: %d, RTC time: %d\n", (int)time_t(), t);
236 t = rtc.GetWakeupTime();
237 stderr.printf("Scheduled alarm: %d, RTC wakeup time: %d\n", (int)alarms.data.deadline, t);
239 zavai.log.info("No alarms left to reschedule");
243 public void check_alarms()
245 last_system_time = time_t();
247 while (alarms != null && alarms.data.deadline <= chosen_time)
249 Alarm a = alarms.data;
251 zavai.log.info("Triggering " + a.label);
258 private void on_gps_time(dynamic DBus.Object pos, int t)
262 last_gps_time_system_time = 0;
265 last_gps_time = (time_t)t;
266 last_gps_time_system_time = time_t();
271 private bool on_system_time()
273 last_system_time = time_t();
278 private void update_time()
280 if (last_gps_time_system_time + 10 > last_system_time)
282 chosen_time = last_gps_time;
283 chosen_type = SourceType.GPS;
287 chosen_time = last_system_time;
288 chosen_type = SourceType.SYSTEM;
290 if (chosen_time / 60 != last_minute)
292 last_minute = chosen_time / 60;
293 minute_changed(chosen_time, chosen_type);
297 /// Request GPS resource
298 public override void start()
302 system_time_timeout = Timeout.add(5000, on_system_time);
303 gps_time.TimeChanged += on_gps_time;
304 last_system_time = time_t();
310 public override void stop()
312 if (!started) return;
314 Source.remove(system_time_timeout);
315 gps_time.TimeChanged -= on_gps_time;
321 public Clock clock = null;
327 zavai.registry.register_service(clock);