Alert on data

You can use triggers to send email notifications or webhook requests when data from the machine is synced, even captured from a specific component with a specified condition. For example, you can configure a trigger to send you a notification when your robot’s sensor collects a new reading.

Follow this guide to learn how to configure a trigger to send webhook requests or emails for the following events:

  • Data has been synced to the cloud: trigger when data from the machine is synced
  • Conditional data ingestion: trigger any time data is captured from a specified component with a specified method and condition

Configure a trigger

To configure a trigger:

  1. Go to the CONFIGURE tab of your machine on the Viam app. Click the + (Create) button in the left side menu and select Trigger.

    The Create menu with Trigger at the bottom of the list of options.
  2. Name the trigger and click Create.

  3. Select trigger Type. Configure additional attributes:

Select the data types for which the trigger should send requests. Whenever data of the specified data types is ingested, a POST request will be sent.

Select the component you want to capture data from and the method you want to capture data from. Then, add any conditions.

These can include a key, a value, and a logical operator. For example, a trigger configured to fire when data is captured from the motor motor-1’s IsPowered method when is_on is equal to True:

Example conditional data ingestion trigger with a condition.

For more information, see Conditions.

  1. Add Webhooks or Emails.

Click Add Webhook. Add the URL of your cloud function or lambda. Configure the time between notifications.

The trigger configured with an example URL in the Viam app.

Click Add Email. Add the email you wish to be notified whenever this trigger is triggered. Configure the time between notifications.

The trigger configured with an example email in the Viam app.

To configure your trigger by using JSON mode instead of Builder mode, paste one of the following JSON templates into your JSON config. "triggers" is a top-level section, similar to "components" or "services".

  "triggers": [
    {
      "name": "<trigger name>",
      "event": {
        "type": "part_data_ingested",
        "data_ingested": {
          "data_types": ["binary", "tabular", "file"]
        }
      },
      "notifications": [
        {
          "type": "webhook",
          "value": "https://1abcde2ab3cd4efg5abcdefgh10zyxwv.lambda-url.us-east-1.on.aws",
          "seconds_between_notifications": <number of seconds>
        }
      ]
    }
  ]
"triggers": [
    {
      "name": "<trigger name>",
      "event": {
        "type": "conditional_data_ingested",
        "conditional": {
          "data_capture_method": "<component>:<name-of-component>:<method>",
          "condition": {
            "evals": [
              {
                "operator": "<lt|gt|lte|gte|eq|neq>",
                "value": <object, string, or int>
              }
            ]
          }
        }
      },
      "notifications": [
        {
          "type": "email",
          "value": "<fill-in-email-here>",
          "seconds_between_notifications": <number of seconds>
        }
      ]
    }
]
{
  "components": [
    {
      "name": "local",
      "model": "pi",
      "type": "board",
      "namespace": "rdk",
      "attributes": {},
      "depends_on": []
    },
    {
      "name": "my_temp_sensor",
      "model": "bme280",
      "type": "sensor",
      "namespace": "rdk",
      "attributes": {},
      "depends_on": [],
      "service_configs": [
        {
          "type": "data_manager",
          "attributes": {
            "capture_methods": [
              {
                "method": "Readings",
                "additional_params": {},
                "capture_frequency_hz": 0.017
              }
            ]
          }
        }
      ]
    }
  ],
  "triggers": [
    {
      "name": "trigger-1",
      "event": {
        "type": "part_data_ingested",
        "data_ingested": {
          "data_types": ["binary", "tabular", "file"]
        }
      },
      "notifications": [
        {
          "type": "webhook",
          "value": "<https://1abcde2ab3cd4efg5abcdefgh10zyxwv.lambda-url.us-east-1.on.aws>",
          "seconds_between_notifications": 0
        }
      ]
    }
  ]
}

The following attributes are available for triggers:

Name Type Required? Description
name string Required The name of the trigger
event object Required The trigger event object:
  • type: The type of the event to trigger on. Options: part_data_ingested, conditional_data_ingested.
  • data_types: Required with type part_data_ingested. The data types that trigger the event. Options: binary, tabular, file, unspecified.
  • conditional: Required with type conditional_data_ingested. See Conditions for more information.
notifications object Required The notifications object:
  • type: The type of the notification. Options: webhook, email
  • value: The URL to send the request to or the email address to notify.
  • seconds_between_notifications: The interval between notifications in seconds.

Conditions

The conditional object for the conditional_data_ingested trigger includes the following options:

Name Type Required? Description
data_capture_method string Required The method of data capture to trigger on.
Example: sensor:<name-of-component>:Readings.
condition object Optional Any additional conditions for the method to fire the trigger. Leave out this object for the trigger to fire any time there is data synced.
Options:
  • evals:
    • operator: Logical operator for the condition.
    • value: An object, string, or integer that specifies the value of the method of the condition, along with the key or nested keys of the measurements in data capture.

Options for operator:

Name Description
lt Less than
gt Greater than
lte Less than or equal to
gte Greater than or equal to
eq Equals
neq Does not equal

Examples:

"condition": {
  "evals": [
    {
      "operator": "lt",
      "value": {
        "Line-Neutral AC RMS Voltage": 130
      }
    }
  ]
}

This eval would trigger for the following sensor reading:

{
  "readings": {
    "Line-Neutral AC RMS Voltage": 100
  }
}
"condition": {
  "evals": [
    {
      "operator": "lt",
      "value": {
        "coordinate": {
          "latitude": 50
        }
      }
    }
  ]
}

This eval would trigger for the following sensor reading:

{
  "readings": {
    "coordinate": {
      "latitude": 40
    }
  }
}
  1. If using a webhook, write your cloud function or lambda to process the request from viam-server. You can use your cloud function or lambda to interact with any external API such as, for example, Twilio, PagerDuty, or Zapier.

Webhooks

Example cloud function

If you are using a cloud function or lambda to process the request from viam-server, you can use this template.

The following example function prints the received headers:

from flask import Flask, request

app = Flask(__name__)


@app.route("/", methods=['GET', 'POST'])
def trigger():
    headers = request.headers
    data = {}
    if request.data:
        data = request.json
    payload = {
        "Org-Id": headers.get('org-id', 'no value'),
        "Organization-Name": headers.get('organization-name', '') or
        data.get('org_name', 'no value'),
        "Location-Id": headers.get('location-id', 'no value'),
        "Location-Name": headers.get('location-name', '') or
        data.get('location_name', 'no value'),
        "Part-Id": headers.get('part-id', 'no value'),
        "Part-Name": headers.get('part-name', 'no value'),
        "Robot-Id": headers.get('robot-id', 'no value'),
        "Machine-Name": headers.get('machine-name', '') or
        data.get('machine_name', 'no value'),
        "Component-Type": data.get('component_type', 'no value'),
        "Component-Name": data.get('component_name', 'no value'),
        "Method-Name": data.get('method_name', 'no value'),
        "Min-Time-Received": data.get('min_time_received', 'no value'),
        "Max-Time-Received": data.get('max_time_received', 'no value'),
        "Data-Type": data.get('data_type', 'no value'),
        "File-Id": data.get('file_id', 'no value'),
        "Trigger-Condition": data.get("trigger_condition", 'no value'),
        "Data": data.get('data', 'no value')
    }
    print(payload)

    return payload


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
import functions_framework
import requests
import time


@functions_framework.http
def hello_http(request):
    headers = request.headers
    data = {}
    if request.data:
        data = request.json
    payload = {
        "Org-Id": headers.get("org-id", "no value"),
        "Organization-Name": headers.get("organization-name", "")
        or data.get("org_name", "no value"),
        "Location-Id": headers.get("location-id", "no value"),
        "Location-Name": headers.get("location-name", "")
        or data.get("location_name", "no value"),
        "Part-Id": headers.get("part-id", "no value"),
        "Part-Name": headers.get("part-name", "no value"),
        "Robot-Id": headers.get("robot-id", "no value"),
        "Machine-Name": headers.get("machine-name", "")
        or data.get("machine_name", "no value"),
        "Component-Type": data.get("component_type", "no value"),
        "Component-Name": data.get("component_name", "no value"),
        "Method-Name": data.get("method_name", "no value"),
        "Min-Time-Received": data.get("min_time_received", "no value"),
        "Max-Time-Received": data.get("max_time_received", "no value"),
        "Data-Type": data.get("data_type", "no value"),
        "File-Id": data.get('file_id', "no value"),
        "Trigger-Condition": data.get("trigger_condition", "no value"),
        "Data": data.get('data', "no value")
    }
    print(payload)

    return 'Received headers: {}'.format(payload)

Returned headers

When an event occurs, Viam sends a HTTP request to the URL you specified for the trigger:

Trigger type HTTP Method
part_data_ingested POST
conditional_data_ingested POST
part_online GET
part_offline GET

The request includes the following headers:

Header Key Description Trigger types
Org-Id The ID of the organization that triggered the request. all
Organization-Name The name of the organization that triggered the request. part_online, part_offline
Location-Id The location of the machine that triggered the request. all
Location-Name The location of the machine that triggered the request. part_online, part_offline
Part-Id The part of the machine that triggered the request. all
Machine-Name The name of the machine that triggered the request. part_online, part_offline
Robot-Id The ID of the machine that triggered the request. all

Returned data

The request body includes the following data:

Data Key Description Trigger types
component_name The name of the component for which data was ingested. part_data_ingested, conditional_data_ingested
component_type The type of component for which data was ingested. part_data_ingested, conditional_data_ingested
method_name The name of the method from which data was ingested. part_data_ingested, conditional_data_ingested
min_time_received Indicates the earliest time a piece of data was received. part_data_ingested
max_time_received Indicates the latest time a piece of data was received. part_data_ingested
method_name The name of the method that triggered the request. conditional_data_ingested
machine_name The name of the machine that triggered the request. part_data_ingested, conditional_data_ingested
location_name The location of the machine that triggered the request. part_data_ingested, conditional_data_ingested
org_name The name of the organization that triggered the request. part_data_ingested, conditional_data_ingested
file_id The id of the file that was ingested. part_data_ingested
trigger_condition The condition that triggered the request. conditional_data_ingested
data The ingested sensor data. Includes metadata with received_at and requested_at timestamps and data in the form map[string]any. part_data_ingested, conditional_data_ingested (sensor data)