Adding support for broadcast-sms to the code-base
[gc-dialer] / src / alarm_handler.py
1 #!/usr/bin/env python
2
3 import os
4 import time
5 import datetime
6 import ConfigParser
7
8 import dbus
9
10
11 _FREMANTLE_ALARM = "Fremantle"
12 _DIABLO_ALARM = "Diablo"
13 _NO_ALARM = "None"
14
15
16 try:
17         import alarm
18         ALARM_TYPE = _FREMANTLE_ALARM
19 except (ImportError, OSError):
20         try:
21                 import osso.alarmd as alarmd
22                 ALARM_TYPE = _DIABLO_ALARM
23         except (ImportError, OSError):
24                 ALARM_TYPE = _NO_ALARM
25
26
27 def _get_start_time(recurrence):
28         now = datetime.datetime.now()
29         startTimeMinute = now.minute + max(recurrence, 5) # being safe
30         startTimeHour = now.hour + int(startTimeMinute / 60)
31         startTimeMinute = startTimeMinute % 59
32         now.replace(minute=startTimeMinute)
33         timestamp = int(time.mktime(now.timetuple()))
34         return timestamp
35
36
37 def _create_recurrence_mask(recurrence, base):
38         """
39         >>> bin(_create_recurrence_mask(60, 60))
40         '0b1'
41         >>> bin(_create_recurrence_mask(30, 60))
42         '0b1000000000000000000000000000001'
43         >>> bin(_create_recurrence_mask(2, 60))
44         '0b10101010101010101010101010101010101010101010101010101010101'
45         >>> bin(_create_recurrence_mask(1, 60))
46         '0b111111111111111111111111111111111111111111111111111111111111'
47         """
48         mask = 0
49         for i in xrange(base / recurrence):
50                 mask |= 1 << (recurrence * i)
51         return mask
52
53
54 def _unpack_minutes(recurrence):
55         """
56         >>> _unpack_minutes(0)
57         (0, 0, 0)
58         >>> _unpack_minutes(1)
59         (0, 0, 1)
60         >>> _unpack_minutes(59)
61         (0, 0, 59)
62         >>> _unpack_minutes(60)
63         (0, 1, 0)
64         >>> _unpack_minutes(129)
65         (0, 2, 9)
66         >>> _unpack_minutes(5 * 60 * 24 + 3 * 60 + 2)
67         (5, 3, 2)
68         >>> _unpack_minutes(12 * 60 * 24 + 3 * 60 + 2)
69         (5, 3, 2)
70         """
71         minutesInAnHour = 60
72         minutesInDay = 24 * minutesInAnHour
73         minutesInAWeek = minutesInDay * 7
74
75         days = recurrence / minutesInDay
76         daysOfWeek = days % 7
77         recurrence -= days * minutesInDay
78         hours = recurrence / minutesInAnHour
79         recurrence -= hours * minutesInAnHour
80         mins = recurrence % minutesInAnHour
81         recurrence -= mins
82         assert recurrence == 0, "Recurrence %d" % recurrence
83         return daysOfWeek, hours, mins
84
85
86 class _FremantleAlarmHandler(object):
87
88         _INVALID_COOKIE = -1
89         _REPEAT_FOREVER = -1
90         _TITLE = "Dialcentral Notifications"
91         _LAUNCHER = os.path.abspath(os.path.join(os.path.dirname(__file__), "alarm_notify.py"))
92
93         def __init__(self):
94                 self._recurrence = 5
95
96                 self._alarmCookie = self._INVALID_COOKIE
97                 self._launcher = self._LAUNCHER
98
99         def load_settings(self, config, sectionName):
100                 try:
101                         self._recurrence = config.getint(sectionName, "recurrence")
102                         self._alarmCookie = config.getint(sectionName, "alarmCookie")
103                         launcher = config.get(sectionName, "notifier")
104                         if launcher:
105                                 self._launcher = launcher
106                 except ConfigParser.NoOptionError:
107                         pass
108
109         def save_settings(self, config, sectionName):
110                 config.set(sectionName, "recurrence", str(self._recurrence))
111                 config.set(sectionName, "alarmCookie", str(self._alarmCookie))
112                 launcher = self._launcher if self._launcher != self._LAUNCHER else ""
113                 config.set(sectionName, "notifier", launcher)
114
115         def apply_settings(self, enabled, recurrence):
116                 if recurrence != self._recurrence or enabled != self.isEnabled:
117                         if self.isEnabled:
118                                 self._clear_alarm()
119                         if enabled:
120                                 self._set_alarm(recurrence)
121                 self._recurrence = int(recurrence)
122
123         @property
124         def recurrence(self):
125                 return self._recurrence
126
127         @property
128         def isEnabled(self):
129                 return self._alarmCookie != self._INVALID_COOKIE
130
131         def _set_alarm(self, recurrenceMins):
132                 assert 1 <= recurrenceMins, "Notifications set to occur too frequently: %d" % recurrenceMins
133                 alarmTime = _get_start_time(recurrenceMins)
134
135                 event = alarm.Event()
136                 event.appid = self._TITLE
137                 event.alarm_time = alarmTime
138                 event.recurrences_left = self._REPEAT_FOREVER
139
140                 action = event.add_actions(1)[0]
141                 action.flags |= alarm.ACTION_TYPE_EXEC | alarm.ACTION_WHEN_TRIGGERED
142                 action.command = self._launcher
143
144                 recurrence = event.add_recurrences(1)[0]
145                 recurrence.mask_min |= _create_recurrence_mask(recurrenceMins, 60)
146                 recurrence.mask_hour |= alarm.RECUR_HOUR_DONTCARE
147                 recurrence.mask_mday |= alarm.RECUR_MDAY_DONTCARE
148                 recurrence.mask_wday |= alarm.RECUR_WDAY_DONTCARE
149                 recurrence.mask_mon |= alarm.RECUR_MON_DONTCARE
150                 recurrence.special |= alarm.RECUR_SPECIAL_NONE
151
152                 assert event.is_sane()
153                 self._alarmCookie = alarm.add_event(event)
154
155         def _clear_alarm(self):
156                 if self._alarmCookie == self._INVALID_COOKIE:
157                         return
158                 alarm.delete_event(self._alarmCookie)
159                 self._alarmCookie = self._INVALID_COOKIE
160
161
162 class _DiabloAlarmHandler(object):
163
164         _INVALID_COOKIE = -1
165         _TITLE = "Dialcentral Notifications"
166         _LAUNCHER = os.path.abspath(os.path.join(os.path.dirname(__file__), "alarm_notify.py"))
167         _REPEAT_FOREVER = -1
168
169         def __init__(self):
170                 self._recurrence = 5
171
172                 bus = dbus.SystemBus()
173                 self._alarmdDBus = bus.get_object("com.nokia.alarmd", "/com/nokia/alarmd");
174                 self._alarmCookie = self._INVALID_COOKIE
175                 self._launcher = self._LAUNCHER
176
177         def load_settings(self, config, sectionName):
178                 try:
179                         self._recurrence = config.getint(sectionName, "recurrence")
180                         self._alarmCookie = config.getint(sectionName, "alarmCookie")
181                         launcher = config.get(sectionName, "notifier")
182                         if launcher:
183                                 self._launcher = launcher
184                 except ConfigParser.NoOptionError:
185                         pass
186
187         def save_settings(self, config, sectionName):
188                 config.set(sectionName, "recurrence", str(self._recurrence))
189                 config.set(sectionName, "alarmCookie", str(self._alarmCookie))
190                 launcher = self._launcher if self._launcher != self._LAUNCHER else ""
191                 config.set(sectionName, "notifier", launcher)
192
193         def apply_settings(self, enabled, recurrence):
194                 if recurrence != self._recurrence or enabled != self.isEnabled:
195                         if self.isEnabled:
196                                 self._clear_alarm()
197                         if enabled:
198                                 self._set_alarm(recurrence)
199                 self._recurrence = int(recurrence)
200
201         @property
202         def recurrence(self):
203                 return self._recurrence
204
205         @property
206         def isEnabled(self):
207                 return self._alarmCookie != self._INVALID_COOKIE
208
209         def _set_alarm(self, recurrence):
210                 assert 1 <= recurrence, "Notifications set to occur too frequently: %d" % recurrence
211                 alarmTime = _get_start_time(recurrence)
212
213                 #Setup the alarm arguments so that they can be passed to the D-Bus add_event method
214                 _DEFAULT_FLAGS = (
215                         alarmd.ALARM_EVENT_NO_DIALOG |
216                         alarmd.ALARM_EVENT_NO_SNOOZE |
217                         alarmd.ALARM_EVENT_CONNECTED
218                 )
219                 action = []
220                 action.extend(['flags', _DEFAULT_FLAGS])
221                 action.extend(['title', self._TITLE])
222                 action.extend(['path', self._launcher])
223                 action.extend([
224                         'arguments',
225                         dbus.Array(
226                                 [alarmTime, int(27)],
227                                 signature=dbus.Signature('v')
228                         )
229                 ])  #int(27) used in place of alarm_index
230
231                 event = []
232                 event.extend([dbus.ObjectPath('/AlarmdEventRecurring'), dbus.UInt32(4)])
233                 event.extend(['action', dbus.ObjectPath('/AlarmdActionExec')])  #use AlarmdActionExec instead of AlarmdActionDbus
234                 event.append(dbus.UInt32(len(action) / 2))
235                 event.extend(action)
236                 event.extend(['time', dbus.Int64(alarmTime)])
237                 event.extend(['recurr_interval', dbus.UInt32(recurrence)])
238                 event.extend(['recurr_count', dbus.Int32(self._REPEAT_FOREVER)])
239
240                 self._alarmCookie = self._alarmdDBus.add_event(*event);
241
242         def _clear_alarm(self):
243                 if self._alarmCookie == self._INVALID_COOKIE:
244                         return
245                 deleteResult = self._alarmdDBus.del_event(dbus.Int32(self._alarmCookie))
246                 self._alarmCookie = self._INVALID_COOKIE
247                 assert deleteResult != -1, "Deleting of alarm event failed"
248
249
250 class _NoneAlarmHandler(object):
251
252         def __init__(self):
253                 self._alarmCookie = 0
254
255         def load_settings(self, config, sectionName):
256                 pass
257
258         def save_settings(self, config, sectionName):
259                 pass
260
261         def apply_settings(self, enabled, recurrence):
262                 pass
263
264         @property
265         def recurrence(self):
266                 return 0
267
268         @property
269         def isEnabled(self):
270                 return False
271
272
273 AlarmHandler = {
274         _FREMANTLE_ALARM: _FremantleAlarmHandler,
275         _DIABLO_ALARM: _DiabloAlarmHandler,
276         _NO_ALARM: _NoneAlarmHandler,
277 }[ALARM_TYPE]
278
279
280 def main():
281         import constants
282         try:
283                 import optparse
284         except ImportError:
285                 return
286
287         parser = optparse.OptionParser()
288         parser.add_option("-x", "--display", action="store_true", dest="display", help="Display data")
289         parser.add_option("-e", "--enable", action="store_true", dest="enabled", help="Whether the alarm should be enabled or not", default=False)
290         parser.add_option("-d", "--disable", action="store_false", dest="enabled", help="Whether the alarm should be enabled or not", default=False)
291         parser.add_option("-r", "--recurrence", action="store", type="int", dest="recurrence", help="How often the alarm occurs", default=5)
292         (commandOptions, commandArgs) = parser.parse_args()
293
294         alarmHandler = AlarmHandler()
295         config = ConfigParser.SafeConfigParser()
296         config.read(constants._user_settings_)
297         alarmHandler.load_settings(config, "alarm")
298
299         if commandOptions.display:
300                 print "Alarm (%s) is %s for every %d minutes" % (
301                         alarmHandler._alarmCookie,
302                         "enabled" if alarmHandler.isEnabled else "disabled",
303                         alarmHandler.recurrence,
304                 )
305         else:
306                 isEnabled = commandOptions.enabled
307                 recurrence = commandOptions.recurrence
308                 alarmHandler.apply_settings(isEnabled, recurrence)
309
310                 alarmHandler.save_settings(config, "alarm")
311                 configFile = open(constants._user_settings_, "wb")
312                 try:
313                         config.write(configFile)
314                 finally:
315                         configFile.close()
316
317
318 if __name__ == "__main__":
319         main()