Incidents

If an incident happens, you'll need a way to react fast and address it. In this section, we will go over how you can create incidents through a workflow.

If enabled, Relay has a built-in workflow called "panic" that is built on top of this Incidents action. Here we'll review just this action.

📘

URN Type for Incidents

Incidents can take either an interaction urn or a device urn as the target.

Creating an Incident

When an incident is created, you'll get a notification on the Relay Dash web console (not on devices, by default) letting you know that there is an active incident. You can see more details on the incident by pressing the View button in Dash. From here, you can see who reported the incident, where it was reported (if location services are enabled), and what time it was reported. There are additional buttons in Dash that can be used at this point, such as Call which will create a full-duplex phone-like call between the Dash computer and the initiator of the incident.

To create an incident, you can use the createIncident() method in your workflow. You provide the URN of the device who is the originator, and a name for the incident. Once this function is run, a notification will appear on Dash.

Resolving an Incident

After a notification appears in Dash, the Dash user can respond to the incident out-of-band, and then mark the incident as resolved in Dash. To resolve, they would need to press the Resolve button in Dash. When an incident is marked resolved, this sends the INCIDENT event with type=resolved to your workflow. When this event is received, in our example workflow we notify the originator that the incident has been resolved.

Example

Here when the workflow is triggered, we create an incident. After the Dash user has resolved the incident in Relay Dash, the originating user is then notified that the incident has been resolved.

import { relay, Event, createWorkflow, Uri } from '@relaypro/sdk'

const app = relay()

const incidentWorkflow = createWorkflow(workflow => {
  const interactionName = 'incident interaction'
  
  workflow.on(Event.START, async(event) => {
    const { trigger: { args: { source_uri } } } = event
    
    // Save the URN of the device that started the incident
    await workflow.setVar('incident_urn', source_uri)
    
    // Create an incident using the createIncident method with the incident URN.
    // This notifies the admin that an incident has occurred.
    await workflow.createIncident(source_uri, 'my incident')
  })

  workflow.on(Event.INCIDENT, async() => {
    # check for type==resolved
    // When the incident has been resolved by administration, an interaction is started so
    // that the user can know the incident has been resolved.
    await workflow.startInteraction(await workflow.getVar('incident_urn'), incidentName)
  })
  
  workflow.on(Event.INTERACTION_STARTED, async({ source_uri: interaction_uri }) => {
    // Let the user know that the incident has been resolved and terminate the workflow
    await workflow.say(interaction_uri, 'The incident has been resolved by admin.')
    await workflow.endInteraction(interaction_uri, interactionName)
  })
  
  relay.on(Event.INTERACTION_ENDED, async() => {
    await relay.terminate()
  })
})
  
app.workflow('incidentpath', incidentWorkflow)
@my_workflow.on_start
async def start_handler(workflow, trigger):

    device_urn = trigger['args']['source_uri']
    target = workflow.make_target_uris(trigger)
    
    # Save the URN of the device that started the incident
    await workflow.set_var("incident_urn", device_urn)
        
    # Create an incident using the createIncident method with the incident URN.
    # This notifies the admin that an incident has occurred.
    await workflow.create_incident(device_urn, 'incident')

@my_workflow.on_incident
async def incident_handler(workflow, type, id, event):
    if type == 'resolved':
        # When the incident has been resolved by administration, an interaction is started so
        # that the user can know the incident has been resolved.
        device = await workflow.get_var('incident_urn')
        target = workflow.targets_from_source_uri(device)
        await workflow.start_interaction(target, 'incident resolved')

@my_workflow.on_interaction_lifecycle
async def lifecycle_handler(workflow, itype, interaction_uri, reason):
    if itype == relay.workflow.TYPE_STARTED:
        # Let the user know that the incident has been resolved and terminate the workflow
        await workflow.say(interaction_uri, 'The incident has been resolved by admin.')
        await workflow.end_interaction(interaction_uri, 'incident resolved')
        
    if itype == relay.workflow.TYPE_ENDED:
        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"];
  
  // Save the URN of the device that started the incident
  await Relay.SetVar(this, "incident_urn", sourceUri);
  
    // Create an incident using the createIncident method with the incident URN.
  // This notifies the admin that an incident has occurred.
  await Relay.CreateIncident(this, sourceUri, "incident");

}

public override async void OnIncident(IDictionary<string, object> dictionary)
{
  // Check for type==resolved.
  // When the incident has been resolved by administration, an interaction is started so
  // that the user can know the incident has been resolved.
  Relay.StartInteraction(this, await Relay.GetVar(this, "incident_urn", "source_uri"), "incident resolved", new Dictionary<string, object>());
}

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

  if (type == InteractionLifecycleType.Started)
  {
    var sourceUri = (string) dictionary["source_uri"];
    
    // Let the user know that the incident has been resolved and terminate the workflow
    await Relay.Say(this, sourceUri, "The incident has been resolved by admin.");
    Relay.EndInteraction(this, sourceUri, "incident resolved");
  }
  else if (type == InteractionLifecycleType.Ended)
  {
    Relay.Terminate(this);
  }
}
@Override
public void onStart(Relay relay, StartEvent startEvent) {
  super.onStart(relay, startEvent);
  String sourceUri = Relay.getSourceUri(startEvent);
  
  // Save the URN of the device that started the incident
  relay.setVar("incident_urn", sourceUri);

  // Create an incident using the createIncident method with the incident URN.
  // This notifies the admin that an incident has occurred.
  relay.createIncident(sourceUri, "incident");
}

@Override
  public void onIncident(Relay relay, IncidentEvent incidentEvent) {
  // Check for type==resolved.
  // When the incident has been resolved by administration, an interaction is started so
  // that the user can know the incident has been resolved.
  relay.startInteraction(relay.getVar("incident_urn", "source_uri"), "incident resolved", null);
}

@Override
  public void onInteractionLifecycle(Relay relay, InteractionLifecycleEvent lifecycleEvent) {
  super.onInteractionLifecycle(relay, lifecycleEvent);
  String interactionUri = lifecycleEvent.sourceUri;

  if (lifecycleEvent.isTypeStarted()) {
    // Let the user know that the incident has been resolved and terminate the workflow
    relay.say(interactionUri, "The incident has been resolved by admin.");
    relay.endInteraction(interactionUri, "incident resolved");
  }
  if (lifecycleEvent.isTypeEnded()) {
    relay.terminate();
  }
}

📘

Built-in Incident Workflow

The Relay system comes with a built-in Panic workflow, that is built upon this Incident primitive. If you have a desire for this capability, check out the built-in workflow to see if it meets your needs before developing your own custom workflow for this.