View Source Triggers

Triggers in Astarte are the go-to mechanism for generating push events. In contrast with AppEngine's REST APIs, Triggers allow users to specify conditions upon which a custom payload is delivered to a recipient, using a specific action, which usually maps to a specific transport/protocol, such as HTTP.

Given this kind of flexibility, triggers are the most powerful way to push data to an external service, potentially without any additional customization.

Triggers can be managed from Realm Management API, astartectl with the astartectl realm-management triggers subcommand, or Astarte Dashboard in the Triggers page.

building-triggers

Building Triggers

Triggers can be either built manually or using Astarte Dashboard's Trigger Editor. Trigger Editor dynamically loads installed Interfaces in the Realm and eases trigger creation by providing not only linting and validation, but also dynamic resolution of Interface names.

Trigger Editor works in a very similar fashion to Interfaces Editor, and shares the same User Interface.

format

Format

A trigger is described using a JSON document. Each trigger is defined by two main parts: condition and action.

This is a JSON representation of an example trigger:

{
  "name": "example_trigger",
  "action": {
    "http_url": "https://example.com/my_hook",
    "http_method": "post"
  },
  "simple_triggers": [
    {
      "type": "data_trigger",
      "on": "incoming_data",
      "interface_name": "org.astarte-platform.genericsensors.Values",
      "interface_major": 0,
      "match_path": "/streamTest/value",
      "value_match_operator": ">",
      "known_value": 0.4
    }
  ]
}

The condition is represented by the simple_triggers array. In this release, Astarte supports only a single entry in the simple_triggers array, but support for multiple simple triggers (and different ways to combine them) is planned for future releases.

The condition in the example specifies that when data is received on the org.astarte-platform.genericsensors.Values interface on /streamTest/value path, if the value of said data is > 0.4, then the trigger is activated. For more information about all the possible conditions, check out the Conditions section

The action object describes what the result of the trigger will be. In this specific case, an HTTP POST request will be sent to https://example.com/my_hook, with the payload:

{
  "timestamp": "<event_timestamp>",
  "device_id": "<device_id>",
  "event": {
    "type": "incoming_data",
    "interface": "org.astarte-platform.genericsensors.Values",
    "path": "/streamTest/value",
    "value": <some_value>
  }
}

To know more about all possible actions, check the Actions section

conditions

Conditions

A condition defines the event upon which an action is triggered. Conditions are expressed through simple triggers. Astarte monitors incoming events and triggers a corresponding action whenever there is a match.

Simple triggers are divided into two types: Device Triggers and Data Triggers.

device-triggers

Device Triggers

Device triggers express conditions matching the state of a device.

This is the generic representation of a Device Trigger:

{
  "type": "device_trigger",
  "on": "<device_trigger_type>",
  "device_id": "<device_id>",
  "group_name": "<group_name>"
}

Parameters

device_trigger_type can be one of the following:

  • device_connected: triggered when a device connects to its transport.
  • device_disconnected: triggered when a device disconnects from its transport.
  • device_error: triggered when data from a device causes an error.

device_id can be used to pass a specific Device ID to restrict the trigger to a single device. * is also accepted as device_id to maintain backwards compatibility and it is considered equivalent to no device_id specified.

group_name can be used to restrict the trigger to all devices that are member of the group.

device_id and group_name are mutually exclusive and if neither of them is specified in the simple trigger, the simple trigger will be installed for all devices in a realm.

data-triggers

Data Triggers

Data triggers express conditions matching data coming from a device.

This is the generic representation of a Data Trigger:

{
  "type": "data_trigger",
  "device_id": "<device_id>",
  "group_name": "<group_name>",
  "on": "<data_trigger_type>",
  "interface_name": "<interface_name>",
  "interface_major": "<interface_major>",
  "match_path": "<match_path>",
  "value_match_operator": "<value_match_operator>",
  "known_value": <known_value>
}

Data triggers are installed for all devices in a Realm.

Data Triggers Parameters

device_id can be used to pass a specific Device ID to restrict the trigger to a single device. * is also accepted as device_id to maintain backwards compatibility and it is considered equivalent to no device_id specified.

group_name can be used to restrict the trigger to all devices that are member of the group.

device_id and group_name are mutually exclusive and if neither of them is specified in the simple trigger, the simple trigger will be installed for all devices in a realm.

data_trigger_type can be one of the following:

  • incoming_data: verifies the condition whenever new data arrives.
  • value_stored: verifies the condition whenever new data arrives, after it is saved to the database.
  • value_change: works only with properties interface; verifies the condition whenever the received value is different from the previous one.
  • value_change_applied: works only with properties interface; verifies the condition whenever the last value received is different from the last one previously received, after it is saved to the database.
  • path_created: verifies the condition whenever a new path in a property interface is set or the first value is streamed on a datastream interface.
  • path_removed: works only with properties interface; verifies the condition whenever a property path is unset.

interface_name and interface_major represent, respectively, the Interface name and major version that uniquely identify an Astarte Interface. interface_name can be * to match all interfaces; in that case interface_major is ignored and all major numbers are matched.

match_path is the path that will be used to match the condition. It can be /* to match all the paths of an interface.

value_match_operator is the operator used to match the incoming data against a known value. It can be * to indicate that all values should be matched (known_value is ignored in that case), otherwise it can be one of these operators: ==, !=, >, >=, <, <=, contains, not_contains. The match is always performed with <incoming_value> <operator> <known_value>. contains and not_contains can be used only with type string, binaryblob and with array types.

known_value is the value used with value_match_operator to perform the comparison on the incoming value. It must have the same type as the incoming value, except in the contains and not_contains case.

actions

Actions

Actions are triggered by a matching condition. An Action defines how the event should be sent to the outer world (e.g. an http POST on a certain URL). In addition, most actions have a Payload, which carries the body of the event.

http-actions

HTTP Actions

Payloads are most of the time represented as text, and Astarte provides several ways to generate them. By default Astarte generates a JSON payload with all the relevant information of the event. This is also the format used when delivering payloads in Astarte Channels. The format of the payload can be found in the Default action section.

Astarte also provides a powerful templating mechanism for plain-text payloads based on top of Mustache. This is especially useful for integrating with third-party actors which require custom plain-text payloads. Keep in mind that Mustache templates are only able to produce text/plain payloads, not valid JSON.

Default action

This is the configuration object representing a minimal default action:

{
  "http_url": "<http_url>",
  "http_method": "<method>"
}

The default action sends an HTTP request to the specified http_url using http_method method (e.g. POST).

Further options might be used, such as "http_static_headers", enabling auth to remote services:

{
  "http_url": "<http_url>",
  "http_method": "<method>",
  "http_static_headers": {
    "Authorization": "Bearer <token>"
  },
  "ignore_ssl_errors": <true|false>
}

The ignore_ssl_errors key is optional and defaults to false. If set to true, any SSL error encountered while doing the HTTP request will be ignored. This can be useful if the trigger must ignore self-signed or expired certificates.

Please, beware that some http headers might be not allowed or reserved for http connection signaling.

simpleevent-payloads

SimpleEvent payloads

The payload delivered in a default HTTP action or in Astarte Channels is a JSON document with this format:

{
  "timestamp": "<timestamp>",
  "device_id": "<device_id>",
  "event": <event>
}

timestamp is an UTC ISO 8601 timestamp (e.g. "2019-10-16T08:56:08.534377Z") representing when the event happened.

device_id identifies the device that triggered the event.

event is a JSON object that has a specific structure depending on the type of the simple_trigger that generated it. Event objects are detailed below.

Additionally, the realm that originated the trigger is available in the request in the Astarte-Realm header.

Event objects
DeviceConnectedEvent
{
  "type": "device_connected",
  "device_ip_address": "<device_ip_address>"
}

device_ip_address is the IP address of the device.

DeviceDisconnectedEvent
{
  "type": "device_disconnected"
}
DeviceErrorEvent
{
  "type": "device_error",
  "error_name": "<error_name>",
  "metadata": {
    "<key>": "<value>"
  }
}

error_name is a string identifying the error. More details can be found in the device errors documentation

metadata is a map with string key and string values that may contain additional information about the error. Some metadata (e.g. binary payloads) might be encoded in base64 if they cannot be represented as string. In that case, the key is prepended with the base64_ prefix.

IncomingDataEvent
{
  "type": "incoming_data",
  "interface": "<interface>",
  "path": "<path>",
  "value": <value>
}

interface is the interface on which data was received.

path is the path on which data was received.

value is the received value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

ValueStoredEvent
{
  "type": "value_stored",
  "interface": "<interface>",
  "path": "<path>",
  "value": <value>
}

interface is the interface on which data was received.

path is the path on which data was received.

value is the received value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

ValueChangeEvent
{
  "type": "value_change",
  "interface": "<interface>",
  "path": "<path>",
  "old_value": <old_value>,
  "new_value": <new_value>
}

interface is the interface on which data was received.

path is the path on which data was received.

old_value is the previous value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

new_value is the received value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

ValueChangeAppliedEvent
{
  "type": "value_change_applied",
  "interface": "<interface>",
  "path": "<path>",
  "old_value": <old_value>,
  "new_value": <new_value>
}

interface is the interface on which data was received.

path is the path on which data was received.

old_value is the previous value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

new_value is the received value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

PathCreatedEvent
{
  "type": "path_created",
  "interface": "<interface>",
  "path": "<path>",
  "value": <value>
}

interface is the interface on which data was received.

path is the path that has been created.

value is the received value. Its type depends on the type of the mapping it's coming from. binaryblob and binaryblobarray type values are encoded with Base64.

PathRemovedEvent
{
  "type": "path_removed",
  "interface": "<interface>",
  "path": "<path>"
}

interface is the interface on which data was received.

path is the path that has been removed.

Mustache action

This is the configuration object representing a Mustache action:

{
  "http_url": "<http_url>",
  "http_method": "<http_method>",
  "template_type": "mustache",
  "template": "<template>"
  "ignore_ssl_errors": <true|false>
}

The Mustache action sends an HTTP request to the specified http_url, with the payload built with the Mustache template specified in template. If the template contains a key inside a double curly bracket (like so: {{ key }}), it will be substituted with the actual value of that key in the event.

The basic keys that can be use to populate the template are:

  • {{ realm }}: the realm the trigger belongs to.
  • {{ device_id }}: the device that originated the trigger.
  • {{ event_type }}: the type of the received event.

The ignore_ssl_errors key is optional and defaults to false. If set to true, any SSL error encountered while doing the HTTP request will be ignored. This can be useful if the trigger must ignore self-signed or expired certificates.

Moreover, depending on the event type, all keys that are contained in the events described in the previous section are available, always by wrapping them in {{ }}.

The realm is also sent in the Astarte-Realm header.

Example

This is an example of a trigger that uses Mustache action.

{
  "name": "example_mustache_trigger",
  "action": {
    "template_type": "mustache",
    "template": "Device {{ device_id }} just connected from IP {{ device_ip_address }}",
    "http_url": "https://example.com/my_mustache_hook",
    "http_method": "post"
  },
  "simple_triggers": [
    {
      "type": "device_trigger",
      "on": "device_connected",
      "device_id": "*"
    }
  ]
}

When a device is connected, the following request will be received by https://example.com/my_mustache_hook:

POST /my_mustache_hook HTTP/1.1
Astarte-Realm: test
Content-Length: 63
Content-Type: text/plain
Host: example.com
User-Agent: hackney/1.13.0

Device ydqBlFsGQ--xZ-_efQxuLw just connected from IP 172.18.0.1

amqp-0-9-1-actions

AMQP 0-9-1 Actions

AMQP 0-9-1 actions might be configured as an alternative to HTTP actions for advanced use cases. AMQP 0-9-1 is the right choice for a number of scenarios, including Astarte Flow integration, high performance ingestion, integration with an existing AMQP infrastructure, etc...

Payloads are always encoded using protobuf, therefore if any other format is required Astarte Flow should be employed as a format converter.

This is a minimal configuration object representing an AMQP 0-9-1 action:

{
  "amqp_exchange": "astarte_events_<realm-name>_<exchange-suffix>",
  "amqp_routing_key": "my_routing_key",
  "amqp_message_expiration_ms": <expiration in milliseconds>,
  "amqp_message_persistent": <true when disk persistency is used>
}

It is possible to configure more advanced AMQP 0-9-1 actions:

{
  "amqp_exchange": "astarte_events_myrealm_myexchange",
  "amqp_routing_key": "my routing key",
  "amqp_static_headers": {
    "key": "value"
  },
  "amqp_message_expiration_ms": 10000,
  "amqp_message_priority": 0,
  "amqp_message_persistent": true,
}

Some Astarte specific restrictions apply:

  • amqp_exchange must have astarte_events_<realm-name>_<any-allowed-string> format.
  • amqp_routing_key must not contain { and }, which are reserved for future uses.

For further details RabbitMQ documentation is suggested.

relationship-with-channels

Relationship with Channels

Channels are part of AppEngine, and allow users to monitor device events through WebSockets, on top of Phoenix Channels. Under the hood, Channels use transient triggers to define which kind of events will flow through a specific room.