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 [DBus (name = "org.enricozini.zavai.Alarm")]
153 public class ZavaiAlarm : Object {
154 public void Notify () {
155 clock.notify("ciao");
159 public class Clock: zavai.Service
161 protected time_t last_gps_time;
162 protected time_t last_gps_time_system_time;
163 protected time_t last_system_time;
164 protected dynamic DBus.Object gps_time;
165 protected uint system_time_timeout;
166 protected time_t last_minute;
167 protected time_t chosen_time;
168 protected SourceType chosen_type;
169 protected AlarmNotification listener;
170 protected ZavaiAlarm listener1;
172 protected dynamic DBus.Object otimed_alarm;
173 protected dynamic DBus.Object rtc;
174 protected SList<Alarm> alarms;
176 // Ticks once a minute
177 public signal void minute_changed(long time, SourceType source);
178 public signal void schedule_changed();
182 Object(name: "clock");
184 listener = new AlarmNotification();
185 listener1 = new ZavaiAlarm();
188 last_gps_time_system_time = 0;
189 last_system_time = time_t();
190 chosen_time = last_system_time;
192 gps_time = zavai.registry.sbus.get_object(
193 "org.freesmartphone.ogpsd",
194 "/org/freedesktop/Gypsy",
195 "org.freedesktop.Gypsy.Time");
198 otimed_alarm = zavai.registry.sbus.get_object(
199 "org.freesmartphone.otimed",
200 "/org/freesmartphone/Time/Alarm",
201 "org.freesmartphone.Time.Alarm");
203 rtc = zavai.registry.sbus.get_object(
204 "org.freesmartphone.odeviced",
205 "/org/freesmartphone/Device/RTC/0",
206 "org.freesmartphone.Device.RealtimeClock");
208 zavai.registry.sbus.register_object("/", listener);
212 public void notify(string label)
214 stderr.printf("HAHA %s\n", label);
217 public Alarm? next_alarm()
224 public void schedule(Alarm a)
226 alarms.insert_sorted(a, alarm_compare);
230 private void otimed_reschedule()
234 zavai.log.info("Scheduling next alarm: " + alarms.data.label + " at " + Time.local(alarms.data.deadline).to_string());
235 zavai.log.info("Scheduling at abs " + "%d".printf((int)alarms.data.deadline));
238 otimed_alarm.ClearAlarm(zavai.registry.bus_name);
240 zavai.log.error("Cannot clear alarms: " + e.message);
243 otimed_alarm.SetAlarm(zavai.registry.bus_name, (int)alarms.data.deadline);
245 zavai.log.error("Cannot reschedule alarms: " + e.message);
248 int t = rtc.GetCurrentTime();
249 stderr.printf("Current time: %d, RTC time: %d\n", (int)time_t(), t);
250 t = rtc.GetWakeupTime();
251 stderr.printf("Scheduled alarm: %d, RTC wakeup time: %d\n", (int)alarms.data.deadline, t);
253 zavai.log.info("No alarms left to reschedule");
257 public void check_alarms()
259 last_system_time = time_t();
261 while (alarms != null && alarms.data.deadline <= chosen_time)
263 Alarm a = alarms.data;
265 zavai.log.info("Triggering " + a.label);
272 private void on_gps_time(dynamic DBus.Object pos, int t)
276 last_gps_time_system_time = 0;
279 last_gps_time = (time_t)t;
280 last_gps_time_system_time = time_t();
285 private bool on_system_time()
287 last_system_time = time_t();
292 private void update_time()
294 if (last_gps_time_system_time + 10 > last_system_time)
296 chosen_time = last_gps_time;
297 chosen_type = SourceType.GPS;
301 chosen_time = last_system_time;
302 chosen_type = SourceType.SYSTEM;
304 if (chosen_time / 60 != last_minute)
306 last_minute = chosen_time / 60;
307 minute_changed(chosen_time, chosen_type);
311 /// Request GPS resource
312 public override void start()
316 system_time_timeout = Timeout.add(5000, on_system_time);
317 gps_time.TimeChanged += on_gps_time;
318 last_system_time = time_t();
324 public override void stop()
326 if (!started) return;
328 Source.remove(system_time_timeout);
329 gps_time.TimeChanged -= on_gps_time;
335 public Clock clock = null;
341 zavai.registry.register_service(clock);