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