Per contact custom SMS sounds in Symbian

By Ghost on Thursday 28 August 2008 15:59
Category: General tricks, Views: 1843

A little over two months ago, I thought it was time to enter the era of the smartphones. After careful consideration I eventually decided to get myself a Nokia N95, which has not let me down since... as long as it was charged. The pim functionality is adequate, gps works as long as you manage to get a lock and it plays my music and podcasts perfectly. It even came with a snazzy headset - yeah, I know, I have low standards in that regard.

However, there was one thing bothering me: although Symbian, the OS running on my phone, is perfectly capable to select different ringtones per contact, it cannot do the same for incoming sms messages. For me, reading sms messages in general has quite a low priority and when busy, I am often not inclined make a run for my mobile whenever I hear an incoming message. I am, though, very interested in sms messages from certain contacts (for example from my girlfriend or my parents). It would be excellent if I could distinguish these high priority messages from the general, less pressing ones. Time to go to the proverbial drawing board.

This problem looked like a perfect opportunity to get into Python programming for Symbian using PyS60. After digging around in the Symbian API, I quickly found the thing I was looking for: callback functionality for the inbox.

code:
1
2
3
4
5
6
7
8
import inbox
listen_inbox = inbox.EInbox

def message_callback(message_id):
    pass

i = inbox.Inbox(listen_inbox)
i.bind(message_callback)

After this script is executed in the interactive console, every time a new message is received by the phone, message_callback gets called with an unique identification for the just received message as a parameter.

Using the message_id parameter, it is fairly trivial to get the exact details of the message itself:

code:
1
2
3
def message_callback(message_id):
    current_inbox = inbox.Inbox(listen_inbox)
    sender = current_inbox.address(message_id)

Like already shown in the example, I am interested in the address of the sender. It is also possible to request the contents, time of arrival or read status of the message.

One important thing to remember is that not all properties are available for every arriving message. The callback is invoked not only for sms messages, but also for incoming bluetooth messages, delivery reports and, if I recall correctly, e-mail messages. For these, the current_inbox.address(message_id) directive will fail. So these exceptions must be caught.

Finally, you need to choose what to do when a specific contact sends a message. I chose to let the telephone speak some text, so it does not interferes with the sound of an incoming message (which I did not seem to be able to mute programmatically :():

code:
1
2
import audio
audio.say(u'New incoming message')

Now the script is starting to look like something. As an additional function, I added a check to see if the phone itself was muted (i.e. the 'silent' profile was selected). The final callback function looks like this:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
def stay_silent():
    return sysinfo.ring_type() == u'silent'    

def message_callback(message_id):
    if stay_silent():
        return
    current_inbox = inbox.Inbox(listen_inbox)
    try:
        sender = current_inbox.address(message_id)
        if (sender == u'Smith John'):
            audio.say(u'New message from John')
    except SymbianError:
        print "Not a sms message, aborting."

Note how the address of the sender already has been processed by the phones contact list, so make sure the same convention is used in the string to check for (in this case 'Lastname Firstname').

Finally, to prevent the script from exiting (so it can run without having an interactive console present), I used a lock and a callback function for the general exit key on the keypad:

code:
1
2
3
4
5
6
7
8
9
10
11
import e32, appuifw
app_lock = e32.Ao_lock()

def quit():
    print "Shutting down..."
    app_lock.signal()

appuifw.app.exit_key_handler = quit
print "Listening for new sms messages"
app_lock.wait()
print "Program finished."

When the exit key is pressed (often the big red 'hang-up' key), the quit function is called, which allows the program to proceed past the app_lock.wait() statement, thus ending the script.

The whole script looks like this:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import inbox, audio, sysinfo
import e32, appuifw
appuifw.app.title = u'sms pounce'

listen_inbox = inbox.EInbox

def stay_silent():
    return sysinfo.ring_type() == u'silent'    

def message_callback(message_id):
    if stay_silent():
        return
    current_inbox = inbox.Inbox(listen_inbox)
    try:
        sender = current_inbox.address(message_id)
        if (sender == u'Smith John'):
            audio.say(u'New message from John')
    except SymbianError:
        print "Not a sms message, aborting."
        

i = inbox.Inbox(listen_inbox)
i.bind(message_callback)


app_lock = e32.Ao_lock()

def quit():
    print "Shutting down..."
    app_lock.signal()

appuifw.app.exit_key_handler = quit
print "Listening for new sms messages"
app_lock.wait()
print "Program finished."

Of course there are lots of fun stuff to further implement. You could, for example, remove the default incoming message sound and let this script handle the notification entirely. Furthermore, having the pounces hardcoded is not that desirable, so a small interface might be nice where new pounces can be added from the address book. I am not sure if I will expand it further, because now it does what it is supposed to do, but who knows... ;) At least it was a fun exercise.

Volgende: Fallout 3 trailer pondering 01-09
Volgende: Kickstarting the PyS60 bluetooth console on Ubuntu 11-08

Comments


By T.net user Yippie, Thursday 28 August 2008 19:24

Wonderfull solution, and might come in very handy. Does it use much of it's memory ?
Do you notice that the app is running @ all ? Keep up the good work :)

By T.net user siepeltjuh, Thursday 28 August 2008 21:59

Why isn't there any 3rd party app allready providing these abilities?

It should be implemented in the default firmware.

By T.net user JW1, Friday 29 August 2008 11:37

When the exit key is pressed (often the big red 'hang-up' key), the quit function is called, which allows the program to proceed past the app_lock.wait() statement, thus ending the script.
If I get it right, when ending a regular phone call (thus pressing the 'hang up' key) your script will also quit I pressume? Or will it only end by shutting down?

By T.net user Ghost, Friday 29 August 2008 16:13

Does it use much of it's memory ?
Do you notice that the app is running @ all ?
Currently I only keep it running in the python console, but I did not notice any significant decrease in battery life. When you open the task manager on the N95 (holding the menu button) it will show up as an active application. Apart from that, it is totally hidden.
If I get it right, when ending a regular phone call (thus pressing the 'hang up' key) your script will also quit I pressume?
The program will only end if you press the 'hang up' key while the application has focus. So it will not terminate when you hang up the phone.

By Matt!, Tuesday 11 November 2008 01:33

I've been after the same thing for a lot of the same reasons, namely that I need distinctive SMS sounds for various different things. I am on call as a sysadmin with the pages going through an email-to-SMS gateway, and am also one of those "textually active" folks.

I've run into a problem though, within the callback I cannot play a sound like so:

import inbox, audio, e32, time
def message_received(msg_id):
time.sleep(5)
box = inbox.Inbox()
sms_text = box.content(msg_id)
print sms_text
audio.say(str(sms_text))
woot=audio.Sound.open('c:\\Data\\starbuck\\alerthigh.wav')
woot.play()

box = inbox.Inbox()
box.bind(message_received)

the sound never plays, even though those two lines work when pasted into the BT console. Any ideas?

By T.net user Ghost, Tuesday 11 November 2008 08:31

I am at work, so I cannot test your code or my solution, but I think it has something to do with trying to play both an audio clip and a text-to-speech segment. I have found my default sms-sound sometimes being muted by the text-to-speech engine. Try disabling one of them and see if that solves your problem.

The sms text from the line before the audio.say-statement is printed to the console, right? So there is no mistake the callback function is actually called upon receiving an sms?

Comment form
(required)
(required, but will not be displayed)
(optional)

Please enter the characters you see in the image below: