04 May 2012

Setting Alarms in Harmattan (Nokia N9)

I have decided to kick start my blogging with a topic not very well covered by the 'official' Meego Harmattan documentation: setting alarms.

The sources of this post are timed documentation only available online here and the timed's simple-client source code. You will have to look at those to do anything more complex than the topics I cover.

Headers and Compilation


You are going to need these headers:
#include <timed/interface>
#include <timed/event>

And to add timed to CONFIG in the project file:
CONFIG += timed

If you are building in Scratchbox (aka Platform SDK) also add libtimed-dev to the Build-Depends in debian/control file.

Simple Event with Alarm


This is pretty easy so first the code:

Maemo::Timed::Event event;
event.setAttribute("APPLICATION", "alarmtest");
event.setAttribute("TITLE", "Get up now!");
event.setAttribute("PLUGIN", "libclockalarm");
event.setBootFlag();
event.setAlarmFlag();
event.setReminderFlag();
// set the alarm in 10s from now
event.setTicker(QDateTime::currentDateTime().toTime_t() + 10);


And some brief explanations:
  • APPLICATION attribute is mandatory and the string has the same rules as C identifiers (alphanumeric and underscore characters only, first character not a number). If you fail these rules no error is reported but the returned cookie is zero and no event is added.
  • TITLE attribute is the title of the alarm (in this case) and will be shown on screen.
  • PLUGIN attribute is the plugin used when the event is triggered. You can write your own plugin but here we are using the one for alarms.
  • setBootFlag() - will ring even if device is off i.e. boots the device if needed.
  • setAlarmFlag() - tells the system it's an alarm not only a scheduled task.
  • setReminderFlag() -  needed to show the default alarm dialog.
  • setTicker() - sets the alarm time in seconds (there are a lot of other 'more human' ways to set this).

Now you just need to add the event to timed's queue. Here is some sample code with error checking:

// timed interface setup
Maemo::Timed::Interface timedIface;
if(!timedIface.isValid()) {
   qDebug() << "Invalid timed interface:" << timedIface.lastError();
   return 1;
}
// add the event
QDBusReply<uint> reply = timedIface.add_event_sync(event);
if(!reply.isValid()) {
   qDebug() << "Adding event failed:" << reply.error().message();
   return 1; 
}
qDebug() << "Added event with cookie:" << reply.value();

Note that you get back a 'cookie' which is in fact the event identifier. This can be later used to search, cancel or change the event.

Event with Command Execution


To do anything more than setting the alarm we need an Action object. Here we are running a command when the user presses the alarm Stop button:

Maemo::Timed::Event::Action &act = event.addAction();
act.setSendCookieFlag();
act.runCommand("echo Event cookie <COOKIE> $(date) >> /tmp/test");
act.whenSysButton(2); // 1 - snooze, 2 - stop

setSendCookieFlag() will cause timed to replace 'COOKIE' or '<COOKIE>' in the command string with the event cookie value.

If you need to run a graphical application there are two other issues to consider. First you need some environment variables set: DISPLAY=:0 and DBUS_SESSION_BUS_ADDRESS (source it from /tmp/session/bus_address.user). Second you need to make sure the application is run as user: set the UID and GID tokens to user/users in aegis manifest file for your application path. Probably the best solution here is to use a wrapper script to start the application.

Also note that timed is a crond replacement so you can schedule tasks to run in the background without user interaction. Recurrences can be done with the Maemo::Timed::Event::Recurrence class.

Cancel and Replace Events


There are 2 very simple API calls for canceling and replacing events based on their cookies:

bool cancel_sync(uint cookie);
uint replace_event_sync(Maemo::Timed::Event newEvent, uint cookie);

5 comments:

  1. Finally someone who seems to know something about timed.

    I'm looking for a way to get the next enabled alarm time from the command line and possibly enable/disable a specific alarm set from the clock application. I know I can enable/disable all alarms with the alarm flag set with:

    dbus-send --system --type=method_call --print-reply --dest=com.nokia.time /com/nokia/time com.nokia.time.enable_alarms boolean:"true/false"

    but this doesn't show up in the UI. The alarm just doesn't go off if disabled with this method. The clock application still shows the alarm(s) as enabled.

    Timed.8.txt talks about "Properties exposed by 'timed' via context framework" and I think it explains why the dbus method fails to show up in the UI. These properties also seem to include a "Time.NextAlarmTime". Can this be queried from the command line somehow?

    I'm not a programmer but I'm trying to write simple scripts to use with Billboard (to get the next active alarm time to show up on the lock screen) and ProfileMatic (to enable/disable alarms and set stuff accordingly with NFC tags)

    ReplyDelete
    Replies
    1. It looks like there is no easy way to get the next enabled alarm. At least I couldn't find any API to do that, and it seems that the timed documentation refers to stuff that was not implemented.

      So you'd probably have to go through all events and check their alarm times. And use cancel or replace calls for canceling a whole event or changing the alarm time of an event.

      Sorry about the late reply... I hope this helps.

      Delete
    2. Thanks for the reply, even if it was a little late.

      By coincidence, on the same day, I finished a python script (started by Thomas Perl) that finds the next enabled clock alarm and posted it on TMO.

      I'm thinking about writing another script in python to replace the state and enabled attributes of an alarm but I'm not sure if that would work with the clock UI. I haven't needed it enough to try it yet ;).

      Thanks again,

      Slarti

      Delete
  2. Hi how are you!! Please make whatsapp for Nokia 9 please

    ReplyDelete