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
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 Clock: zavai.Service
141 protected time_t last_gps_time;
142 protected time_t last_gps_time_system_time;
143 protected time_t last_system_time;
144 protected dynamic DBus.Object gps_time;
145 protected uint system_time_timeout;
146 protected time_t last_minute;
147 protected time_t chosen_time;
148 protected SourceType chosen_type;
149 protected ZavaiClock dbusClock;
151 protected dynamic DBus.Object otimed_alarm;
152 protected dynamic DBus.Object rtc;
153 protected SList<Alarm> alarms;
155 // Ticks once a minute
156 public signal void minute_changed(long time, SourceType source);
157 public signal void schedule_changed(Alarm? next);
158 public signal void alarm_triggered(string label);
162 Object(name: "clock");
164 dbusClock = new ZavaiClock();
167 last_gps_time_system_time = 0;
168 last_system_time = time_t();
169 chosen_time = last_system_time;
171 gps_time = zavai.registry.sbus.get_object(
172 "org.freesmartphone.ogpsd",
173 "/org/freedesktop/Gypsy",
174 "org.freedesktop.Gypsy.Time");
177 otimed_alarm = zavai.registry.sbus.get_object(
178 "org.freesmartphone.otimed",
179 "/org/freesmartphone/Time/Alarm",
180 "org.freesmartphone.Time.Alarm");
182 rtc = zavai.registry.sbus.get_object(
183 "org.freesmartphone.odeviced",
184 "/org/freesmartphone/Device/RTC/0",
185 "org.freesmartphone.Device.RealtimeClock");
187 zavai.registry.sbus.register_object("/org/enricozini/Zavai/Clock", dbusClock);
190 public void notify_alarm(string label)
192 stderr.printf("Notifying %s\n", label);
193 alarm_triggered(label);
194 schedule_changed(next_alarm());
197 public Alarm? next_alarm()
200 ev = at.earliestID("z");
201 if (ev.deadline == 0)
203 string label = Alarm.getLabel(ev.id);
204 Alarm res = new Alarm();
210 public void schedule(string timespec, string label) throws Error
212 Alarm.schedule(timespec, label);
213 schedule_changed(next_alarm());
216 private void on_gps_time(dynamic DBus.Object pos, int t)
220 last_gps_time_system_time = 0;
223 last_gps_time = (time_t)t;
224 last_gps_time_system_time = time_t();
229 private bool on_system_time()
231 last_system_time = time_t();
236 private void update_time()
238 if (last_gps_time_system_time + 10 > last_system_time)
240 chosen_time = last_gps_time;
241 chosen_type = SourceType.GPS;
245 chosen_time = last_system_time;
246 chosen_type = SourceType.SYSTEM;
248 if (chosen_time / 60 != last_minute)
250 last_minute = chosen_time / 60;
251 minute_changed(chosen_time, chosen_type);
255 /// Request GPS resource
256 public override void start()
260 system_time_timeout = Timeout.add(5000, on_system_time);
261 gps_time.TimeChanged += on_gps_time;
262 last_system_time = time_t();
268 public override void stop()
270 if (!started) return;
272 Source.remove(system_time_timeout);
273 gps_time.TimeChanged -= on_gps_time;
279 public Clock clock = null;
285 zavai.registry.register_service(clock);