Notifications

There are 2 types of notifications that you can use on the Relay platform: Broadcast and Alert. They are similar, but have slightly different behaviors. In this section we'll go through these different types of notifications and some use cases.

📘

URN Type for Notifications

Because notifications will typically be used for a group of devices, it is designed to not need an interaction to exist with the group members when calling these methods. Thus, the target URN to use with notifications is a group URN or a device URN.

You can use the name of the group that you would like to relay your message to, and pass that into the utility method called groupName(). The target should look like this after it is constructed from the function:
urn:relay-resource:name:group:MyGroupName

You can find more information on helpful functions for constructing group URNs in the Groups section.

Broadcast

Broadcast is a way to send a single message out to other Relay users no matter what channel they are in. The specified message will be preceded with an attention chime sound and then played out once via text-to-speech. Additionally, if your browser currently has Dash logged in and loaded, you'll get a popup-notification with text in your browser. This command can be sent to a whole group or targeted to a specific device.

There are 5 things that need to be passed in to the broadcast function:

  • The target, who you want to send your broadcast to. If you are sending out a broadcast to a group, you need to create a URN from the group name. We have a helper function for this, called groupName(). You just need to pass in the name of the group that you would like to create a URN for.
  • The originator, the device URN of who is sending out the broadcast message to the group. Even if this is being sent out programmatically without touch a device, one device does need to be designated as the originator.
  • The name, a unique identifier for that specific broadcast instance. The name has to be a unique string.
  • The text to be broadcasted as audio via text-to-speech. The text has to be a string.
  • Push options: this is only for when the broadcast goes out to the companion app that runs on a smartphone and if you would like to specify additional content to appear in the smartphone's pull-down notification tray. If you would just like the defaults, you can put '{}' for this parameter.

Below is an example workflow that sends out a broadcast with the message 'Hi team, this is a test broadcast' to the 'Engineering' group.

...
workflow.on(Event.START, async(event) => {
  const { trigger: { args: { source_uri } } } = event
  
   // Get the target from the name of the group that you want to broadcast your message to
  const target = Uri.groupName('Engineering')
  
  // Broadcast a message to the target
  await workflow.broadcast(target, source_uri, 'broadcast_test', 'Hi team, this is a test broadcast', {})
  
  // Terminate the workflow after the message is broadcasted
  await workflow.terminate()
})
...
...
@my_workflow.on_start
async def start_handler(workflow, trigger):
    source_uri = trigger['args']['source_uri']
    
    # Get the target from the name of the group that you want to broadcast your message to
    target = relay.workflow.Relay.group_name('Engineering')
    
    # Broadcast a message to the target
    await workflow.broadcast(target, source_uri, "broadcast_test", "Hi team, this is a test broadcast", {})
		
    # Terminate the workflow after the message is broadcasted
    await workflow.terminate()
...
...
public override async void OnStart(IDictionary<string, object> dictionary)
  {
    var trigger = (Dictionary<string, object>) dictionary["trigger"];
    var triggerArgs = (Dictionary<string, object>) trigger["args"];
    var sourceUri = (string) triggerArgs["source_uri"];

    // Get the target from the name of the group that you want to broadcast your message to
    string target = RelayUri.GroupName("Engineering");

    // Broadcast a message to the target
    await Relay.Broadcast( this, target, sourceUri, "broadcast_test", "Hi team, this is a test broadcast");

    // Terminate the workflow after the message is broadcasted
    Relay.Terminate(this);
  }
...
...
@Override
public void onStart(Relay relay, StartEvent startEvent) {
  super.onStart(relay, startEvent);

  String sourceUri = Relay.getSourceUri(startEvent);
  // Get the target from the name of the group that you want to broadcast your message to
  String target = RelayUri.groupName("Engineering");

  // Broadcast a message to the target
  relay.broadcast(target, sourceUri, "broadcast_test", "Hi team, this is a test broadcast");

  // Terminate the workflow after the message is broadcasted
  relay.terminate();
}
...
...
api.OnStart(func(startEvent sdk.StartEvent) {
  sourceUri := api.GetSourceUri(startEvent)
  // Get the target from the name of the group that you want to broadcast your message to
  target := sdk.GroupName("Engineering")

  var notificationOptions sdk.NotificationOptions
  // Can set notification option fields
  // Broadcast a message to the target
  api.Broadcast(target, sourceUri, "broadcast_test", "Hi team, this is a test broadcast", notificationOptions)

  // Terminate the workflow after the message is broadcasted
  api.Terminate();
})
...

Once the broadcast command runs, the targeted devices will hear the message once. There won't be any repeats of the message, or acknowledgement required.

However, if you'd like to ensure that your message is repeated until it is acknowledged, this is where Alerts are useful, see below.

Alerts

Alerts are similar to a Broadcast, but have the additional feature of requiring an acknowledgement from the user, and the message and chime are repeated every 30 seconds until it is acknowledged. A user can acknowledge it by tapping the Talk button.

The Alert method takes the same 5 parameters as the Broadcast method.

The alert will repeat the chime and message every 30 seconds until the alert is acknowledged or cancelled from the originating workflow. To cancel an alert, use the relay.cancelAlert() function. Using this function would cancel the alert on all of the remaining devices that the alert was sent out to (some devices may have already acknowledged the alert). This would be useful in a number of different scenarios.

Perhaps you need to send out an alert of "Cleanup needed on aisle 3" to the Cleaning group. When one person acknowledges the event, you want to stop the alert on all devices; we wouldn't want everyone in the group to go cleanup aisle 3. Once one person picks up the request, the alert should stop on all other devices.

Here is a code based example of that:

...
const myGroupName = "Main"
const myInteractionName = "thanks interaction"
const myAlertName = "cleanup_alert"

const notifyWorkflow = createWorkflow(workflow => {

  workflow.on(Event.START, async (event) => {
    const { trigger: { args: { source_uri } } } = event

    // This gets the target from the name of the group that you want to alert
    const target = Uri.groupName(myGroupName)
    // Alert the group with your message
    await workflow.alert(target, source_uri, myAlertName, "Cleanup needed on aisle 3", {})
  })

  workflow.on(Event.NOTIFICATION, async (notifEvent) => {
    // Get the name of the device that is responding to the alert
    const responder = Uri.parseDeviceName(notifEvent.source_uri)
   
    // Get the target from the name of the group in which you want to stop the alert
    const target = Uri.groupName(myGroupName)
   
    // Use the relay.cancel_alert() function to cancel the alert on all devices now that one has acknowledged
    await workflow.cancelAlert(target, myAlertName)

    // let the team know someone acknowledged
    await workflow.broadcast(target, notifEvent.source_uri, 'acknowledged', `${responder} is responding to the alert`, {})

    // tell only the responder "thank you"
    const responder_target = notifEvent.source_uri
    await workflow.startInteraction(responder_target, myInteractionName)
  })

  workflow.on(Event.INTERACTION_STARTED, async ({ source_uri }) => {
    await workflow.sayAndWait(source_uri, `thank you for accepting the cleaning task`)
    await workflow.endInteraction([source_uri])
  })

  workflow.on(Event.INTERACTION_ENDED, async() => {
    await workflow.terminate()
  })
})
...
...
my_group_name = 'Main'
my_interaction_name = 'thanks interaction'
my_notification_name = 'cleanup_alert'


@my_workflow.on_start
async def start_handler(workflow, trigger):
    originator = trigger['args']['source_uri']

    # This gets the target from the name of the group that you want to alert
    target = relay.workflow.group_name(my_group_name)
    # Alert the group with your message
    await workflow.alert(target, originator, my_notification_name, "Cleanup needed on aisle 3", {})


@my_workflow.on_notification
async def notification_handler(workflow, event, name, response, source_uri):
    # Get the name of the device that is responding to the alert
    responder = relay.workflow.parse_device_name(source_uri)
    
    # Get the target from the name of the group in which you want to stop the alert
    target = relay.workflow.group_name(my_group_name)
    
    # Use the relay.cancel_alert() function to cancel the alert on all devices now that one has acknowledged
    await workflow.cancel_alert(target, 'cleanup_alert')

    # let the team know someone acknowledged
    await workflow.broadcast(target, source_uri, 'acknowledged', f'{responder} is responding to the alert', {})

    # tell only the responder "thank you"
    responder_target = relay.workflow.Relay.targets_from_source_uri(source_uri)
    await workflow.start_interaction(responder_target, my_interaction_name)


@my_workflow.on_interaction_lifecycle
async def lifecycle_handler(workflow, itype, interaction_uri, reason):
    if itype == relay.workflow.TYPE_STARTED:
        # should run only on the responder
        await workflow.say_and_wait(interaction_uri, 'thank you for accepting the cleaning task')
        await workflow.end_interaction(interaction_uri)
    if itype == relay.workflow.TYPE_ENDED:
        await workflow.terminate()
...
...
        private static string myGroupName = "Main";
        private static string myInteractionName = "thanks interaction";
        private static string myAlertName = "cleanup_alert";


        public NotifyWorkflow(Relay relay) : base(relay)
        {
        }

        public override async void OnStart(IDictionary<string, object> startEvent)
        {
            var trigger = (Dictionary<string, object>)startEvent["trigger"];
            var triggerArgs = (Dictionary<string, object>)trigger["args"];
            var sourceUri = (string)triggerArgs["source_uri"];

            // This gets the target from the name of the group that you want to alert
            string target = RelayUri.GroupName(myGroupName);
            // Alert the group with your message
            await Relay.Alert(this, target, sourceUri, myAlertName, "Cleanup needed on aisle 3");
        }

        public override async void OnNotification(IDictionary<string, object> dictionary)
        {
            string sourceUri = (string)dictionary["source_uri"];

            // Get the name of the device that is responding to the alert
            string responder = RelayUri.ParseDeviceName(sourceUri);

            // Get the target from the name of the group in which you want to stop the alert
            string target = RelayUri.GroupName(myGroupName);

            // Use the relay.cancel_alert() function to cancel the alert on all devices now that one has acknowledged
            await Relay.CancelAlert(this, sourceUri, myAlertName, target);

            // let the team know someone acknowledged
            await Relay.Broadcast(this, target, sourceUri, "acknowledged", $"{responder} is responding to the alert.");

            // tell only the responder "thank you"
            Relay.StartInteraction(this, sourceUri, myInteractionName);
        }


        public override async void OnInteractionLifecycle(IDictionary<string, object> lifecycleEvent)
        {
            var type = (string)lifecycleEvent["type"];

            if (type == InteractionLifecycleType.Started)
            {
                var sourceUri = (string)lifecycleEvent["source_uri"];
                await Relay.SayAndWait(this, sourceUri, "thank you for accepting the cleaning task");
                Relay.EndInteraction(this, sourceUri);
            }
            else if (type == InteractionLifecycleType.Ended)
            {
                Relay.Terminate(this);
            }
        }
...
...
        private final String myGroupName = "Main";
        private final String myInteractionName = "thanks interaction";
        private final String myAlertName = "cleanup_alert";

        @Override
        public void onStart(Relay relay, StartEvent startEvent) {
            String sourceUri = Relay.getSourceUri(startEvent);

            // This gets the target from the name of the group that you want to alert
            String target = RelayUri.groupName(myGroupName);
            // Alert the group with your message
            relay.alert(target, sourceUri, myAlertName, "Cleanup needed on aisle 3");
        }

        @Override
        public void onNotification(Relay relay, NotificationEvent notificationEvent) {
            // Get the name of the device that is responding to the alert
            String responder = RelayUri.parseDeviceName(notificationEvent.sourceUri);

            // Get the target from the name of the group in which you want to stop the alert
            String target = RelayUri.groupName(myGroupName);

            // Use the relay.cancel_alert() function to cancel the alert on all devices now that one has acknowledged
            relay.cancelAlert(target, myAlertName);

            // let the team know someone acknowledged
            relay.broadcast(target, notificationEvent.sourceUri, "acknowledged", responder + " is responding to the alert");

            // tell only the responder "thank you"
            String responderTarget = notificationEvent.sourceUri;
            relay.startInteraction(responderTarget, myInteractionName);
        }

        @Override
        public void onInteractionLifecycle(Relay relay, InteractionLifecycleEvent lifecycleEvent) {
            String interactionUri = (String)lifecycleEvent.sourceUri;
            if (lifecycleEvent.isTypeStarted()) {
                relay.say(interactionUri, "thank you for accepting the cleaning task");
                relay.endInteraction(interactionUri);
            }
            if (lifecycleEvent.isTypeEnded()) {
                relay.terminate();
            }
        }
...
...
  var myGroupName string = "Main"
  var myInteractionName string = "thanks interaction"
  var myAlertName string = "cleanup_alert"
  var notificationOptions sdk.NotificationOptions

  api.OnStart(func(startEvent sdk.StartEvent) {
    sourceUri := api.GetSourceUri(startEvent)
    // This gets the target from the name of the group that you want to alert
    target := sdk.GroupName(myGroupName)
    // Alert the group with your message
    api.Alert(target, sourceUri, myAlertName, "Cleanup needed on aisle 3", notificationOptions)
  })

  api.OnNotification(func(notificationEvent sdk.NotificationEvent) {
    // Get the name of the device that is responding to the alert
    responder := sdk.ParseDeviceName(notificationEvent.SourceUri)
    
    // Get the target from the name of the group in which you want to stop the alert
    target := sdk.GroupName(myGroupName)
    
    // Use the api.CancelAlert() function to cancel the alert on all devices now that one has acknowledged
    api.CancelAlert(target, myAlertName)
    
    // let the team know someone acknowledged
    api.Broadcast(target, notificationEvent.SourceUri, "acknowledged", responder + " is responding to the alert", notificationOptions)

    // tell only the responder "thank you"
    responderTarget := notificationEvent.SourceUri
    api.StartInteraction(responderTarget, myInteractionName)
  }) 

  api.OnInteractionLifecycle(func(interactionLifecycleEvent sdk.InteractionLifecycleEvent) {
    if interactionLifecycleEvent.LifecycleType == "started" {
      interactionUri := interactionLifecycleEvent.SourceUri
      api.Say(interactionUri, "thank you for accepting the cleaning task", sdk.ENGLISH)
      api.EndInteraction(interactionUri);
    }

    if interactionLifecycleEvent.LifecycleType == "ended" {
      api.Terminate()
    }
  })
...

And here is example of the NOTIFICATION event, where one device has acknowledged the alert (not when the alert is initially announced across the group):

{ "event": "ack_event",
  "name": "my_alert",
  "notification_state": {
    "_type": "wf_api_notification_state",
    "acknowledged": ["urn:relay-resource:name:device:Relay%20Valet3"],
    "cancelled": [],
    "created": ["urn:relay-resource:name:device:Relay%20Valet3", "urn:relay-resource:name:device:user%3A7"],
    "timed_out": []
  },
  "source_uri": "urn:relay-resource:name:device:Relay%20Valet3"
}

In the above event:

  • "source_uri" element: the device that was listed as the originator of the alert.
  • "created" element": list of devices on which the alert was created.
  • "timed_out" element: list of devices on which the alert timed out being created, for example if the device is powered off or outside of network coverage.
  • "acknowledged" element: list of devices that have acknowledged the alert.
  • "cancelled" element": list of devices where the alert has been cancelled.

Note that only one notification can be active at a time per device. For example, if there is an active alert that hasn't yet been cancelled / acknowledged on a device, you won't be able to broadcast to it until the active alert is first cancelled / acknowledged.