Alarms and Messages

Using alarms and messages to wake up the browser

The Senza platform operates in two modes:

  1. The interactive mode in which a web browser is running and your code can run.
  2. The video streaming mode in which the browser is not running, and your code stops running.

As we've learned in the article on seamlessly switching between web apps and streaming video, it's possible to programmatically switch from interactive to video streaming mode. If the viewer presses any button on the remote control, Senza will switch back into interactive mode and resume running the web browser.

But that relies on the user intervention. What if we want to programmatically switch back? Senza provides two ways to deliver a message to the browser:

  1. The Alarm Manager lets the web app schedule a message to be delivered at a specific time.
  2. The Message Manager lets you make an API call from an external system to deliver a message. You can send a message to a single device or a group of devices.

When you receive either one of these messages you can then switch back to the foreground if desired.

Alarm Manager

Normally if you want to call a function after a period of time, you can call setTimeout(). The problem with doing that when the browser is not running is that your scheduled timeouts will not wake up the browser. Instead, Senza offers the Alarm Manager to help you trigger events at a specific time. It lets you specify the name of an event, the time that the event should be sent back to the browser, and some data.

Where you would normally call:

setTimeout(() => { doSomething(data); }, seconds * 1000)

With Alarm Manager you can set an alarm like this:

alarmManager.addEventListener("something", (event) => { doSomething(event.detail); }); alarmManager.addAlarm("something", Date.now() + seconds * 1000, data);

There are a few differences to note:

  • You can't pass function to the alarm manager, just an event name. You can then define an event listener function for that event.
  • Whatever data you pass when setting the alarm will be passed back in the detail property on the event.
  • Rather than taking a number of milliseconds, it takes a timestamp. Simply add the desired time interval to Date.now().

Example

For this tutorial, we'll start with the banner app from seamlessly switching between web apps and streaming video. It has almost all the functionality we need, and we'll modify just a few lines of code.

We'll add a new behavior where if you press the up button, it will switch to the background, wait fifteen seconds, switch back to the foreground and display a new banner.

First add an up() key handler:

document.addEventListener("keydown", async function(event) { switch (event.key) { case "ArrowLeft": left(); break; case "ArrowRight": right(); break; case "ArrowUp": up(); break; case "Enter": await enter(); break; default: return; } event.preventDefault(); });

When the viewer presses the up button, we'll add an alarm called showBanner that will be called in fifteen seconds, and pass in the new text we want to display. Then we'll fade out and move to the background as before when the user presses the OK button.

function up() { alarmManager.addAlarm("alarm", Date.now() + 15000, "THIS IS A NEW BANNER SET BY AN ALARM!"); fadeOut(moveToBackground); }

Then we'll add a listener for the event. We'll replace the text of the banner with the data from the event, and move back to the foreground.

import { alarmManager } from "senza-sdk";

alarmManager.addEventListener("alarm", (event) => { banner.innerHTML = event.detail; lifecycle.moveToForeground(); });

To try it out, run the updated app and press the up arrow. You'll see the banner fade out and the streaming video start playing. After fifteen seconds, the banner will appear back in with the new message. Voila!

Messages Manager

That's great if you want the app to send a delayed message to itself, but what if you want to send a message to the app from an external system? We can use the Message Manager API to do exactly that.

You can send a message in one of two ways:

  1. You can send a message directly to a single device using the Device Messaging API.
  2. You can send a message to multiple devices using the Group Messaging API. An app can register as part of a group, and it will receive any messages sent to that group.

See the article on making authenticated API requests for all the details on how to obtain an access token and use it to call the messages API. Read that and come back here!

Setting up the app

First, import the messageManager from the library.

import { messageManager } from "senza-sdk";

If you want to receive direct messages, you'll need to look up the deviceId of your device. For development, you can find this in the operations console by clicking on your device. In production, your app can get this by programmatically using the getDeviceInfo() function.

If you want to receive group messages, after your call to init(), call the registerGroups() method with an array of one or more group names.

messageManager.registerGroups(["Banner"]);

Sending a device message

To send a device message, call the API as follows with the deviceId in the URL.

POST https://hyperscale-message-broker-main.ingress.active.streaming.synamedia.com/message-broker/1.0/messages/devices/{deviceId}

{ "payload": "THIS IS A NEW BANNER SET BY THE API!", "target":"application", "origin":"internal", "eventName": "BannerMessage" }

Sending a group message

To send a group message, call the API as follows with the tenantId in the URL.

POST https://hyperscale-message-broker-main.ingress.active.streaming.synamedia.com/message-broker/1.0/messages/tenant/{tenantId}/groups/app

{ "groups": ["Banner"], "payload": "THIS IS A NEW BANNER SET BY THE API!", "eventName": "BannerMessage" }

Receiving messages

From that point, receiving a message is pretty similar to the alarm manager.

The interface is the same whether you are receiving a device message or a group message.

We don't know if the app will be in the background or foreground when the message arrives, so we should check the lifecycle state and move to the foreground if necessary. Then we can update the banner message.

messageManager.addEventListener("message", async (event) => { const currentState = await lifecycle.getState(); if (currentState == "background" || currentState == "inTransitionToBackground") { lifecycle.moveToForeground(); } banner.innerHTML = event.detail.payload; });

Try making some calls to the API while the app is in the foreground or the background and check that it works. Either way, you'll see the new message show up in the app!