Sending Windows Phone 7 Notifications from Ruby

Windows Phone 7 supports push notifications that let you do things like send alerts to a WP7 application, or update the tile (icon) for an application. The notifications are sent using the Microsoft Push Notification Service (MPNS), a free service that Microsoft provides. You’re reponsible for somehow creating the notification message and passing it to the MPNS, which then routes it to the WP7 device.

I’ve been reading how to accomplish this using .NET and wanted to give it a go using Ruby. Turns out it’s pretty simple.

Notification Types

There are three types of notifications that WP7 supports, toast, tile, and raw. Each has a specific function, so we need to understand what they do and when to use them. You can see an example of each at https://msdn.microsoft.com/en-us/library/ff402558(v=vs.92).aspx.

A toast notification results in an alert that pops up on the screen for 10 seconds, but doesn’t disrupt what the user is doing. If you tap the notification message, the associated application will launch. This type of notification is useful for alerts or other informational type messages that you want the user to see, but not neccessarily act on right now.

A tile notification changes the tile (icon) for the application, if the tile is pinned to the start screen. You can update the title, a numerical counter, or even change the image of the tile. There’s two sides to every tile with the latest mango update, and you can set both sides. This is a useful way to provide information without having the user open the application. For example, an e-mail application might update a number to indicate the number of new messages.

A raw notification is simply a push of data into the application. It’s up to the application as far as what it does with this data.

Notification URI

To send a notification, you need to know the URI to send the notification to. This needs to be created by an application running on the WP7 device. The ‘How It Works’ section of https://msdn.microsoft.com/en-us/library/ff402558(v=vs.92).aspx has more information on this, but basically the application talks to the Microsoft Push Notification Service, which gives it a unique URI, which it then hands off to your notification service.

I’m not going to concentrate too much on the Windows Phone 7 application in this post, as that’s not really the focus. For testing I used the Windows Phone 7 client appilcations in the SDK Notification samples available at https://msdn.microsoft.com/en-us/library/ff431744(v=VS.92).aspx#BKMK_PushAndTiles.

Notification Messages

The notification message is sent to the MPNS via HTTP post. There are some specific headers that must be set, and the body format depends on the notification type being sent. The common items for all message types are the following HTTP headers:

  • Content-Type = text/html
  • Content-Length = length of the message
  • X-MessageID = unique ID
  • X-NotificationClass = int value

The first two should be familiar to anyone who’s worked with HTTP, but what are these X-MessageID and X-NotificationClass things?

X-MessageID is an optional header that lets you specify a unique ID for this message, and you can then compare it to the value returned in the message response to match up response to header.

X-NotificationClass is where we specifiy how important this message is so that the MPNS can batch up message based on importance and send them to your phone in one lump. This saves on phone battery life as it just has to receive the one batch instead of X individual notifications.

But what value do we use here? Took me a bit to find it, but here’s the valid values:

  • 1 – Immediate Tile notification
  • 2 – Immediate Toast notification
  • 3 – Immediate Raw notification
  • 11 – Within 450 seconds Tile notification
  • 12 – Within 450 seconds Toast notification
  • 13 – Within 450 seconds Raw notification
  • 21 – Within 900 seconds Tile notification
  • 22 – Within 900 seconds Toast notification
  • 23 – Within 900 seconds Raw notification

If you’re sending a raw message, this is pretty much all you need; headers, body, URI to send to. It’s a little more complicated for toast and tiles.

Toast message structure

Toast notification messages have the following message format:

 <?xml version='1.0' encoding='utf-8'?>
<wp:Notification xmlns:wp='WPNotification'>
    <wp:Toast>
        <wp:Text1></wp:Text1>
        <wp:Text2></wp:Text2>
        <wp:Param></wp:Param>
    </wp:Toast>
</wp:Notification>

For more information on toast notifications, see https://msdn.microsoft.com/en-us/library/ff402558(v=vs.92).aspx.

Tile message structure

Tile notification messages have the following message format:

 <?xml version='1.0' encoding='utf-8'?>
<wp:Notification xmlns:wp='WPNotification'>
    <wp:Tile>
        <wp:BackgroundImage></wp:BackgroundImage>
        <wp:Count></wp:Count>
        <wp:Title></wp:Title>
        <wp:BackBackgroundImage></wp:BackBackgroundImage>
        <wp:BackTitle></wp:BackTitle>
        <wp:BackContent></wp:BackContent>
    </wp:Tile>
</wp:Notification>

For more information on tile notifications, see https://msdn.microsoft.com/en-us/library/ff402558(v=vs.92).aspx.

WP7 Push Notifications from Ruby

The following code demonstrates how to accomplish push notifications from Ruby:

 require 'net/http'
require 'uri'

NONRAWTYPES=[:toast, :tile]
BASEBATCH={:tile=>1, :toast=>2, :raw=>3}
BATCHADDS={:delay450=>10, :delay900=>20}

def send_notification(params)
    msg=build_message(params)
    delay=calculate_delay(params[:type],params[:delay])

    uri=URI.parse(params[:uri])
    http=Net::HTTP.new(uri.host, uri.port)
    headers={
        'Content-Type'=>'text/html',
        'Content-Length'=>msg.length.to_s,
        'X-NotificationClass'=>delay.to_s
    }
    if NONRAWTYPES.include?(params[:type])
        headers['X-WindowsPhone-Target']= (params[:type]==:toast) ? 'toast' : 'token'
    end

    http.post(uri.path, msg, headers)
end

private
def calculate_delay(type, delay)
    if type.nil?
        type = :raw     end
    delay.nil? ? BASEBATCH[type] : BASEBATCH[type]+BATCHADDS[delay]
end

def build_message(params)
    if NONRAWTYPES.include?(params[:type])
        msg_body="<?xml version='1.0' encoding='utf-8'?><wp:Notification xmlns:wp='WPNotification'><wp:#{params[:type].capitalize}>"
        case params[:type]
        when :toast
            msg_body << "<wp:Text1>#{params[:title]}</wp:Text1>"
            msg_body << "<wp:Text2>#{params[:message]}</wp:Text2>"
            msg_body << "<wp:Param>#{params[:param]}</wp:Param>"
        when :tile
            msg_body << "<wp:BackgroundImage>#{params[:image]}</wp:BackgroundImage>"
            msg_body << "<wp:Count>#{params[:count].to_s}</wp:Count>"
            msg_body << "<wp:Title>#{params[:title]}</wp:Title>"
            msg_body << "<wp:BackBackgroundImage>#{params[:back_image]}</wp:BackBackgroundImage>"
            msg_body << "<wp:BackTitle>#{params[:back_title]}</wp:BackTitle>"
            msg_body << "<wp:BackContent>#{params[:back_content]}</wp:BackContent>"
        end
        msg_body << "</wp:#{params[:type].capitalize}></wp:Notification>"
    else
        msg_body=params[:message]
    end
    return msg_body
end

To use this, you can pass the following keys and values to send_notification:

  • :uri=> the notification URI of the device/application. This is generated by an application on a WP7 device.
  • :type=> the type of notification. It really only looks for :tile and :toast; :raw is assumed if you don’t specify :type.
  • :title=> the title of the notification. This is valid for both :tile and :toast notification types.
  • :message=> The message for :toast or :raw notification types.
  • :count=> Specifies the value for the numeric badge on tiles. Only valid for :tile notification types.
  • :param=> The parameter to be passed to the WP7 application when a user touches a toast notification. Only valid for :toast notification types.
  • :image=> The url of the background image for tiles. Only valid for :tile notification types.
  • :back_image=> The url of the back background image for tiles. Only valid for :tile notification types.
  • :back_content=> The text of the content message for the back of a tile. Only valid for :tile notification types.
  • :back_title=> The text of the title of the back of a tile. Only valid for :tile notification types.
  • :delay=> The batch delay for sending the message from the MPNS. Either :delay450 or :delay900; if :delay is not specified the interval is ‘immediate’.

To try this out

I tested this using the notification samples from the Windows Phone 7 SDK. You can download these from https://msdn.microsoft.com/en-us/library/ff431744(v=VS.92).aspx#BKMK_PushAndTiles. These samples do require Visual Studio 2010 running on a Windows machine, but they function just fine from the emulator; no WP7 device required. Note that none of the samples upload their URI, but instead rely on displaying it and having you manually type it in. 

The steps I followed to use this were:

  1. Launch the WP7 application in the emulator and copy the URI generated by the application.
  2. Invoke send_notification with parameters appropriate to the notification type. For example, for a toast notification I passed :uri=>“url from app”, :type=>:toast, :title=>‘foo’, :message=>‘bar’.
  3. Notice the effect in the WP7 application.

In a real world scenario, you’d instead post the value up to a web service. For example, storing the URI along with the rest of your user data. You could then query for a subset of users, perhaps those with the same zip code for a weather application, and bulk send notifications to all of them.

As always, let me know if there are problems or questions on this, or if there's a better way to accomplish this.