Writing custom sensors and actuators | Waylay.io Documentation
cURL JavaScript


Waylay is a cloud-based agent architecture that observes its environment via software-defined sensors and acts on its environment through software-defined actuators. A (very) high level blog about it you can find here blog. Smart agent concept

In short, if you implement a weather sensor, it should return a state such as “Rain” or “Sunny”, but it can as well provide information (rawData) such as temperature, humidity etc. Every sensor must either return state or rawData (it can also return both). Obviously, in order to execute a sensor, you will need some input, like city. How to declare what you need in the plug, and how the framework will provide this input to the sensor will be explained later in the document.

For the actuator implementation, there is no need to return any data, you just need to fire a script and that’s it, this can be an SMS, mail, tweet, REST call or any other thing.

Actuator must be attached to sensors or gates, and it gets triggered when a particular condition is met. That condition can be a state or the state change of a sensor or gate. That implies that only sensors that return states can be linked to actuators.

Note: there is also possibility to write java based plugs, but this feature is only supported in OEM package. Java based plugs can be of your interest in case you want to write complex mathematical calculations which can’t be handled by the Functional node. For more information how to write such plugs, please e-mail to info@waylay.io

Javascript plugs

Via waylay platform you can write plugins in javascript. This is a sensor editor window that you get in the app:

Sensor view


Every script is executed in the sandbox environment with a number of packages pre-installed. You can simply access them from the script, no need to require them. You have access to these (and only these) NPM packages:

Important thing to mention is that your script (both for sensors and actuators) MUST send back a value, regardless whether it was successful or not. So try to catch issues and send them back if you want. To send things back, call the method send(). If you call it without arguments, that is fine, but in case that this was a sensor, well then it is not that great. If you need a working example, just click new sensor or new actuator in the app and editor with default implementation will appear on the screen.

In case of a sensor implementation, you want to send back a result, so you must call a method send(null, value), where value is a JSON object with:

Sandbox custom packages

From sandbox version 1.17.3 you can add custom packages in your environment. These packages can either reside in the public npm registry or a private registry, in which case, please contact support@waylay.io to configure it for you.

Sensor view

When loading your own custom package, you need to use require in the script:

const { get, random } = require('lodash')
const { format } = require('date-fns')

const lower = get(options, 'requiredProperties.lower', 0)
const upper = get(options, 'requiredProperties.upper', 10)

const timestamp = format(new Date(), 'DD/MM/YYYY')
const number = random(lower, upper)

send(null, {
  rawData: { number, timestamp }

In case that you use the same package which is already available in the sandbox in your plug (like in this example lodash), please be aware that the package defined in the depencies will be in use.


If you start by clicking “Create Sensor”, you can see the following script created for you:

// sensors should never throw exceptions but instead send an error back

if(options.requiredProperties.testProperty1) {
  var randomValue = Math.random();
  value = {
    observedState: randomValue > 0.5 ? "hello" : "world",
    rawData: {  message: "message", x: 2.34, random: randomValue, property: options.requiredProperties.testProperty1}
  send(null, value);
  send(new Error("Missing property testProperty1"));

After execution, every sensor must return a state (sensor must defined at least two states in its metadata), and optionally rawData measurements. For instance, if you implement a weather sensor, it should return a state such as “Rain” or “Sunny”, but it can as well provide information (rawData) such as temperature, humidity etc. Obviously, in order to execute a sensor, you will need some input, like city. How to declare what you need in the plug, and how the framework will provide this input to the sensor will be explained later in the document. You can create sensors to acquire data from physical devices, databases, applications or online services. You do this by means of writing Javascript and defining metadata. Waylay provides many examples which you can use as a baseline to create your own sensors, specific to your application. On a technical level, a sensor can be considered as a function that, when called, returns the state it is in.


A sensor has two possible outputs:

Output State

Each sensor has a limited amount of discrete states which it can be in, eg ON/OFF or LOW/MEDIUM/HIGH. These states will be used when logic is applied. As an example, for the temperature sensor, you could define states as HOT (>30C), WARM (20C-30C), MILD (10C-20C), COLD (0C-10C) and FREEZING (<0C). The sensor then returns the state information back to the logic and you can start building logic using these states.

Output Raw Data

This is the data that was collected or pushed in its raw form, like continuous value parameters such as eg temperature, light and memory used.In some cases, you may also want to use this raw data in the mathematical preprocessing step of your logic. Therefore this data is stored in the task context that can be used in your logic.

As you can see, this is just simple javascript, nothing fancy. The only thing you need to know at this point is that these scripts will be executed by a Node.js server, somewhere in the cloud. More precisely Node.js 4.x LTS. For details on supported ES6 api you can visit node.green.

Sensor example

This sensor is also handy if you want to get familiar with boolean gates or if you want to test formula computation using a Function node.

var randomValue = Math.random();
var state;
if(randomValue > 0.85)
  state = "ONE";
else if(randomValue > 0.7)
  state = "TWO";
else if(randomValue > 0.55)
  state = "THREE";
else if(randomValue > 0.4)
  state = "FOUR";
else if(randomValue > 0.25)
  state = "FIVE";
  state = "SIX";
value = {  
    observedState:  state,
    rawData : {  random: randomValue}  

In this example, this sensor will roll the dice, return one of the six states, and also return a random value. Random value can later been access via the RAW Data context (see below).

Returning value from the sensor

Example of the sensor response

    "rawData": {

Note that you always must return the value from the script. It is either a state, or rawData or both.

Below you see an example of a result of a sensor invocation returned by the waylay webapp:


You create an actuator the same way you create a sensor.

If you start by clicking “Create Actuator”, you can see the following script created for you:

// actuators should never throw exceptions but instead send an error back

  console.log("hello " + options.requiredProperties.testProperty1);
  send(new Error("Missing property testProperty1"));

Actuator example

This actuator creates a zendesk ticket.

var username =  options.globalSettings.ZENDESK_USER;
var token = options.globalSettings.ZENDESK_KEY;
var subject = options.requiredProperties.subject;
var message = waylayUtil.template(options, options.requiredProperties.message);
var domain = options.globalSettings.ZENDESK_DOMAIN || "waylay";


if(username && token && subject && message){

  var url = "https://"+domain+".zendesk.com/api/v2/tickets.json";
  var data = {
      "ticket" : {
          "requester" : {
              "name" : "waylayPlatform",
          }, "subject" : subject,
              "comment" : message
  var options = {
    url: url,
    json: data,
        user: username+"/token",
        pass: token

  var callback = function(error, response, body) {
    if (!error && (response.statusCode == 200 || response.statusCode == 201)) {
      send(new Error(JSON.stringify(response)));

  request.post(options, callback);
  send(new Error("Missing properties"));

Similar to the sensor framework, Waylay provides a builtin framework that support actuators towards different systems. Actuators allow to execute actions based on the outcome of rules. Actuators are triggered as the result of the sensor execution (sensor state, or state changes). Writing actuator code is very similar to writing the sensor call. The only exception is that actuars are “fire and forget calls”. They don’t return states or rawData. In case you want to pass some data to the task context, please check this link. This actuator also makes use of global settings and template call from utility package

Plugin Metadata

    {"name": "StockPrice",
        "description": "Stock exchange sensor, stock price value",
        "author": "Veselin",
        "version": "1.0.1",
        "iconURL": "http://app.waylay.io/icons/stock.png",
        "documentationURL": "",
        "category": "Stock",
        "states": [
        "configuration": [
                "name": "threshold",
                "type": "DOUBLE",
                "mandatory": true,
                "sensitive": false
                "name": "stock",
                "type": "STRING",
                "mandatory": true,
                "sensitive": false
        "rawData": [
                "parameter": "volume",
                "dataType": "double",
                "collectedType": "instant",
                "unit": "double",
                "isObject": true
                "parameter": "high",
                "dataType": "double",
                "collectedType": "instant",
                "unit": "double",
                "isObject": true
                "parameter": "low",
                "dataType": "double",
                "collectedType": "instant",
                "unit": "double",
                "isObject": true
                "parameter": "price",
                "dataType": "double",
                "collectedType": "instant",
                "unit": "double",
                "isObject": true
                "parameter": "moving_average",
                "dataType": "double",
                "collectedType": "computed",
                "unit": "double",
                "isObject": true
                "parameter": "percent",
                "dataType": "double",
                "collectedType": "instant",
                "unit": "double",
                "isObject": true

As part of a sensor/actuator plugin definition, you also need to provide metadata. If you use waylay editor, metadata will be created automatically. For sensor plugins the following metadata needs to be defined:

For actuator plugins, the following metadata needs to be defined:

These metadata of the sensors and actuators are also exposed over the REST interface.

For instance, this is a REST response of one sensor:


Every plug (both actuator and sensor) can access the context. Why is that needed? Let’s imagine that you want to create a Mail actuator. You will need some information to create an email (subject, content, from, to etc…), and you will need API keys (if you use Mandrill for instance) or SMTP server settings. Plug should be able to tell this to the framework, and that is partially done via the metadata properties as described above. Basically there are five ways plugs can access additionally information at runtime:


For instance, in the code you retrieve properties like this:

var url = options.requiredProperties.url

These properties are normally provided to the script at configuration time. Typical examples of properties are URLs, API keys or connection settings.

You are also required to provide this info in the metadata of the plug (right hand side of the editor), i.e. these Required Properties are not automatically parsed from the JavaScript.

Properties declarative binding

Declarative context data binding in Waylay engine provides a powerful way to connect context data to properties values (input properties of the plugs). By using declarative binding it is possible to avoid usage of the special sandbox utilities functions. It also provides a way to inject only part of context information into the scripting engine. This is important in evolution of waylay scripting engine from custom sandbox implementation to more generic FaaS solutions, like Amazon Lambda, OpenFaaS,etc.

Declarative binding syntax

Properties binding object:

  "globalSettings": {
    "API_KEY": "1234",
    "API_SECRET": "xxx",
    "FROM_PHONE": "+32222222",
    "WITH SPACE": "spaced"
  "vault": {
    "mykey": "dddd"
  "streamdata": {
    "temperature": 23.88023798012594,
    "timestamp": 1597922146444
  "node": {
    "name": "ANDGate_1",
    "triggeringNode": {
      "nodeid": "streamingDataSensor_2",
      "nodeState": "Below"
  "nodes": {
    "streamingDataSensor_1": {
      "state": "Equal",
      "rawData": {
        "parameter": 21,
        "threshold": 21,
        "data": {
          "temperature": 21,
          "timestamp": 1606900553030
        "collectedTime": 1606900552513,
        "state": "Equal",
        "resource": "sensor1"
      "resource": {
        "meta": {
          "id": "sensor1",
          "name": "Outdoor temperature",
          "threshold": 21,
          "lastUpdate": "Thu Sep 24 11:52:11 CEST 2020"
    "streamingDataSensor_2": {
      "state": "Below",
      "rawData": {
        "parameter": 21,
        "threshold": 23,
        "data": {
          "temperature": 21,
          "timestamp": 1606900745679
        "collectedTime": 1606900895631,
        "state": "Below",
        "resource": "sensor2"
      "resource": {
        "meta": {
          "id": "sensor2",
          "name": "Indoor temperature",
          "threshold": 23,
          "lastUpdate": "Thu Sep 24 11:52:13 CEST 2020"
  "task": {
    "TASK_ID": "2514673a-6ebb-47a3-81cf-49916cdf7b14",
    "NODE_TRIGGERED_NAME": "streamingDataSensor_2",
    "NODE_NAME": "ANDGate_1",
    "ACTION_NODE": "ANDGate_1",
    "resources": {
      "indoor": {
        "meta": {
          "id": "sensor2",
          "name": "Indoor temperature",
          "threshold": 23,
          "lastUpdate": "Thu Sep 24 11:52:13 CEST 2020"
      "outdoor": {
        "meta": {
          "id": "sensor1",
          "name": "Outdoor temperature",
          "threshold": 21,
          "lastUpdate": "Thu Sep 24 11:52:11 CEST 2020"

The syntax make use of tags surrounded by ${} or $${} symbols, eg ${streamdata.temperature}.

The tag between curly brackets consist from following parts:

Prefix Description
globalSettings points to a specific global setting. For example ${globalSettings.OPENWEATHER_KEY} will return OPENWEATHER_KEY configured in global settings. You can get only one key at request: ${globalSettings..*} will return execution error
vault allows to get encrypted vault key value. You can get only one key at request. ${vault..*} binding will return execution error
streamdata the payload which triggered the sensor execution
node Will contain data specific for the node that is currently executing
node.name name of the current node
node.resourceId the resource id of the resource bound to the current node - not there if no resource is bound
node.resource.meta the metadata of resource associated with current node
node.triggeringNode.nodeId if the current node was triggered by another node, the node name of that triggering node
node.triggeringNode.nodeState if the current node was triggered by another node, the state of that triggering node
nodes Will contain data for nodes that have already been executed
nodes.<nodeid>.state the last state of the node with name <nodeid>
nodes.<nodeid>.rawData raw data that has been returned by the last execution of node <nodeid>
nodes.<nodeid>.resource.meta the metadata of resource that is bound the node with name <nodeid> (only available for nodes that have executed)
task that section contain basic task attributes: TASK_ID, RESOURCE, NODE_TRIGGERED_NAME, NODE_TRIGGERED_STATE, NODE_NAME
task.resource.meta.<metakey> the metakey of the task resource
task.resources.<resourcerole>.meta.<metakey> the metakey of the resource associated to the resourcerole of the task

If in begin of binding definition only one $ symbol used then returned result will be a JSON primitive converted to string using following rules:

If two $$ symbols are used in begin of binding definition then returned JSON object or value will be returned as stringified JSON string.

Example bindings: During task/sensor/actuator execution special JSON object for properties binding is created.

Binding Result Description
$${streamdata..*} “{ \“temperature\": 23.88023798012594,\“timestamp\": 1597922146444}” returns whole sensor payload as stringified JSON object
${streamdata.temperature} “23.88023798012594” get temperature element value of sensor payload
${nodes.streamingDataSensor_1.state} “Equal” get state streamingDataSensor_1 node in template
${globalSettings.API_KEY} “1234” get API_KEY global setting
${globalSettings.[‘WITH SPACE’]} “spaced” getting properties with spaces
${vault.mykey} “dddd” get value of mykey encrypted vault setting
$${nodes.streamingDataSensor_1.resource.meta} “{\“id\": \“sensor1\”,\“name\": \“Outdoor temperature\”,\“threshold\": 21,\“lastUpdate\": \“Thu Sep 24 11:52:11 CEST 2020\"}” get metadata of the resource of node streamingDataSensor_1
${task.resources.outdoor.meta.threshold} 21 get metadata of the resource bound to the templated resource with resourcerole $outdoor
${node.resource.meta.linkToSomeOtherObject.thresholdX} 23.7 get meta data value from a referenced resource, e.g. “linkToSomeOtherObject” : {“$ref”:"/resources/yourResource”}

Another example usage is on a scriptSensor which gets a script object and evaluates it. It expects a script property with javascript code. You can set multiline value for that property with binding injection like that:

const x = $${node.resource.meta}

const rv={ observedState: "Done", rawData: { data: x } }

send(null, rv)

The below picture shows you an example on how to use declarative context binding in a function node. The function node takes its input straight from the context and therefore does not require a streamBridgeData or streamingDataSensor in front. Hence, it simplifies your rule logic. Context Binding


If actuator (or sensor) wants to sent a mail and is using another templating framework (eg. https://github.com/webdevelopers-eu/jquery-dna-template) it can use also it’s own variable binding. For example following property definition:

Dear ${first} ${last} ...

In that case to avoid Waylay declarative binding parser you can escape it using ‘\’ sign. So example property definition should be:

Dear \${first} \${last} ...

Error handling

You can use the ? symbol after the opening bracket to mark that JSON path execution can return an empty result in case if JSON path expression points to unexistent data.
eg. ${?globalSettings.xxx} will return empty string if no xxx global setting defined.

If the ? symbol was not provided, the sensor (or actuator) execution will fail with an error.

Global settings

You can also declare global settings which are available to sensors and actuators. These settings are visible in the profile page. This way you can for instance declare API keys that rarely change. When you define sensor or actuator, you can access these settings this way (where you need to replace the KEY with the exact key you have declared before):


for instance you can write a code that requires a token like this:

var token = options.globalSettings.token

This way you can decide whether you want to provide a token at the time you start a task from a template, or you want to declare it in the global settings.

Raw Data

If you want to get a temperature of the node Home, you would need to get it like this:

var temperature = options.rawData.Home.temperature

Now you can combine first two things together, so ask at configuration time a node name and then ask for temperature, by declaring that you need a node name in the Required Properties of the webscript:

var temperature, nodeName = options.requiredProperties.node;
if (nodeName && options.rawData[nodeName]) {
  temperature = options.rawData[nodeName].temperature;

Every plug can access raw data at runtime, even when that raw data was collected by another sensor. The only limitation is that you need to know the name of the node that you want to get the data from. Later we shall see how we can access rawData using waylay utility package.

The raw data is provided in the form options.rawData[node_name]

You can also access the current state and the last execution time of the sensor:

This is useful in case you want for instance to do a sequence computation on the states of the nodes, for more info, see later section on this.

This is not ideal, but in case you need this, let me directly show you the code of one possible actuator implementation, that is only sending JSON object:

send(null, {message: "hello world"});

Notice that I was sending back value with a message. Normally, actuators are “fire and forget” by calling only send() , but this one is sending back a value - attaching the result in the context to the node to which this actuator belongs! Later, you might want to do restore with this backup file:

var nodeName = options.requiredProperties.node;
if(!options.actuatorData || !options.actuatorData[nodeName]){
    console.info("nothing to do");
    var value = options.actuatorData[nodeName].message;
    /*you get back "hello world"
    .... your code goes here */

Similar to raw data context, an actuator also can push some (limited) results back to the task context.

Note: we will learn later how do the same with one liner, using waylay utility package.

Task Data

In your plug code, you access these settings this way:

var resource = options.task.RESOURCE
var task = options.task.TASK_ID
var node_name = options.task.NODE_NAME

In case of an actuator or node triggered by an other node you can request the tiggering node name and it’s state

var node_triggered_name = options.task.NODE_TRIGGERED_NAME
var node_triggered_state = options.task.NODE_TRIGGERED_STATE

For instance, if you want to create an actuator that would control the running task (please see REST documentation for more info), this is how you can do it this way:

var taskID = options.requiredProperties.taskId || options.task.TASK_ID;
var command = options.requiredProperties.command;
var username =  options.globalSettings.API_KEY;
var password = options.globalSettings.API_PASS;

    'https://'+ username + ':' + password +'@app.waylay.io/api/tasks/'+taskID+'/command/'+command,
    function (error, response, body) {
        if (!error && response.statusCode == 200) {
        } else
            send(new Error("Error executing the action"));

In the task context, you can retrieve the following task-related data:

Resource metadata

If you have a task that “runs” on following resource

  "id": "mydevice_id4",
  "customer": "tenant1",
  "label": "helloWorld",
  "location" : {
    "lat" : 51,
    "lon": 3.71

you can retrieve the customer field of the task resource in the sensor/actuator as

var resourceCustomer = waylayUtil.getProperty(options, 'META.$.customer')

The metadata of all resources of the task, is also available for plugins through the task context. You can access the metadata in your script plug using

The Waylay engine makes sure that the resource metadata in the task context is up-to-date.

Internal state

A plugin can keep internal state around between invocations, for example if it wants to aggregate values over time or if the next invocation depends on the result of the previous one.

For this purpose the options passed in to the plugin can have a property internalState containing a json compatible value. The presence of his property indicates that a previous execution stored some information for the next execution.

The example below shows how you would create a counter actuator that sends out debug messages

var previousCount = options.internalState || 0
var newCount = previousCount + 1
send(null, {
  message: "We are at " + newCount,
  internalState: newCount

The waylay client

The sandbox enviroment provides a waylay global variable that contains a preconfigured Waylay client object from the Waylay Javascript SDK.

This gives you access to all REST exposed waylay services, simply by calling functions. All interfaces are described here

This waylay client is preconfigured to have the correct authentication and endpoint configuration for your tenant.

Here is the example of one sensor that fetches resource metadata:

var thing  = waylayUtil.getProperty(options, "resource") || waylayUtil.getResource(options)

    send(null, {observedState : "Collected" , rawData : response})
.catch(err =>{
    //send(new Error(err))
    send(null, {observedState : "Not Collected"})

Utility functions

The name of the package is waylayUtil. This package provides 4 types of utility functions:

Retrieve raw data

to retrieve rawData as JSON for the node “nodeName”

var rawData =  waylayUtil.getRawData(options, "nodeName");

to retrieve parameter “parameterName” from rawData of the node “nodeName”.

var param =  waylayUtil.getRawData(options, "nodeName", "parameterName");

With this function you can either retrieve complete rawData as JSON object (2 arguments) of another node, or just the parameter of that node (3 arguments).

Unlike other calls in the util package, waylayUtil.getRawData throws the exception if the data is not available. The reason is that we assume that when you call getRawData you really expect this data always to be available to you. Otherwise, actuator, or other sensor that expect data from another sensor will not be functioning correctly. That also allows you to see the errors in a debugger window or in the Actuator logger.

Retrieve cached raw data

Raw data and state of previous sensor/actuator execution are available via this call.

to retrieve parameter “latitude” from rawData of the node that invoke the script. This way you can for instance cache things that you need to retrieve only once (like location API).

var rawData =  waylayUtil.getCacheData(options, "latitude");

This call is handy if you have a parameter that doesn’t change between consecutive calls, such as geolocation of the fix location. In case you used API key to retrieve this location, makes little sense to invoke 3rd party REST endpoint every time sensor is executed.

For instance: you want to fetch geo location for a given address via the API service. Once the call is successful, in the result you can provide as the raw data “latitude” and “longitude”. Next time, these values will be automatically in the rawData of that node, so by simply calling waylayUtil.getCacheData on both parameters will give you this information, avoiding a need to call REST call again.

Example where you can toggle the state of the sensor based on its previous execution:

var previous = waylayUtil.getCacheData(options, "state");
var newValue = "1";
if(previous === undefined || previous === "1"){
    newValue = "0";
console.log("previous: " + previous + ", new: " + newValue);
var result = {
    observedState: newValue
send(null, result);

Retrieve stream data

Example: to retrieve parameter “latitude” from the stream data for the node that has resource matching the stream resource (which sends parameter “latitude”).

var rawData =  waylayUtil.getStreamData(options, "latitude");

In the following example, we want to make sure that the sensor returns the error, if it was executed by a task/node tick (via polling or cron). In that case, the stream object is empty.

var streamdata = waylayUtil.getStreamData(options);
    send(new Error("No Streamdata"));
      var  value = {
            observedState: newState,
            rawData: {
                streamData: streamdata
    send(null, value);

If you have configured a node to get executed when new data arrives, you can retrieve that data using this call. If the sensor is executed by a task/node tick (via polling or cron), the stream object is empty.

Retrieve resource name

var resourceName =  waylayUtil.getResource(options);

When you associate the sensor with a given resource name, you can use this function in the sensor code to retrieve that name. Please keep in mind that some words are reserved:

This call will automatically translate $, META.$.<key> and $taskId into the runtime resource name.

Retrieve input property

var rawData =  waylayUtil.getProperty(options, "city");

Function to retrieve input parameter. This is a simple shortcut for the call: options.requiredProperties.city, with one addition, this call also tries to unwrap the argument in case it comes from another node via task context (options). For instance, if you have defined in the plug the input property city, then you can either later (at design time or via REST) provide to that property value such as London or you can provide input like ANOTHER_NODE.city, where ANOTHER_NODE is a sensor which “produces” a city as the rawData result. See picture below:

currentWeather sensor fetches the input city like this:

var city = waylayUtil.getProperty(options, "city")

Retrieve sensor name

Function to retrieve the name of the sensor (inside the script, you can find what is the node name assosiated with this call)

var node =  waylayUtil.getMySensorName(options);

Retrieve attached sensor name

Function to retrieve the name of the sensor that triggered the execution of this sensor.

var node =  waylayUtil.getAttachedSensorName(options);

Retrieve OAuth2 token for a given user and provider

Function to retrieve OAuth2 token for a given user and provider. This function is a promise!

waylayUtil.getAuthTokens(options, user, provider)
.then(tokens => {
    console.log(JSON.stringify(tokens, null, 2))
    // ...
.catch(error => {
    send(new Error(error))

Evaluate inputs (eval)

var rawValue =  waylayUtil.evaluateData(options, input);

With this call, you can create a formula, with input in this format:

<node1.rawdat1> or <node1.rawdat1> OPER <node2.rawdat2>

Compared to Function node eval function can’t make statistical computations.

JSONPath expression

waylay util packages uses JSONPath expression to select rawdata, with some small addiotions, have a look at examples:

Accessing the array of data points, or array of objects:

var diff = waylayUtil.evaluateData(options,
    <node1.temperature> + <node3.items[0].temperature>)

this way you can access the last point in the array:

var diff = waylayUtil.evaluateData(options,
    <node1.temperature> + <node3.items[(@.length-1)].temperature>)

If the array is array of objects, you can continue till the value you are interested in:

var diff = waylayUtil.evaluateData(options,
    <node1.temperature> + <node3.items2[(@.length-1)].item.temperature>)

You can also use selectors:

var diff = waylayUtil.evaluateData(options,
    <node3.items3[?(@.name == 'piet')].temperature> + <node3.items3[?(@.name == 'veselin')].temperature>)

You can also make a sentences this way (which is interesting thing to do if you want to send the actuator message with the content data from other sensors):

var temp = waylayUtil.evaluateData(options,
    "Temperature is " + <node3.items3[?(@.name == 'piet')].temperature>)

If you want to filter on a parameter that is greater or lower than a particular value, you must use &lt and &gt notation, for instance get all temperatures with values greater than 23 degrees:

var temp = waylayUtil.evaluateData(options,
    <node3.items3[?(@.temperature &gt 23)].temperature>)

You can also retrieve an array:

var array = waylayUtil.evaluateData(options, "<node3.items.*>")

There are also some small extensions to the library, mostly operations on arrays: To get a count of elements:

var count = waylayUtil.evaluateData(options, "<node3.items[count]>")

You can also use some stats, in case that you select the array:

var diff = waylayUtil.evaluateData(options,
    <node3.data[max]> - <node3.data[min]>)

Produce a string from an array:

var str = waylayUtil.evaluateData(options, "<node3.text[stringify]>")

Produce a string from an array, joined by “and”

var str = waylayUtil.evaluateData(options, "<node3.text[stringify, and]>")


Template call using handlebars template language

var message = waylayUtil.template(options, "Hello {{node.rawID}}")

In case you want to use actuators such as e-mail, you might wish to create an HTML based content. In that case, you might want to use template call rather than evaluateData call. Note also that in order to provide rawData input, we use other delimiters {{}}. For more info, please check handlebarsjs documentation.

If you want to specify resource name in template, use {{RESOURCE}} If you want to access resource metadata property in template, use {{ META.$.<metadata property> }}

We also provide a few handlebars helper functions, which are annotated by {{{}}}:

Distance function

Distance function

var dist = waylayUtil.getDistance(options, "nodeName1", "nodeName2");

You can use this function for nodes that have geo location in the raw data in format:

You can simply call the function this way, by providing all data:

var dist = waylayUtil.getDistance(lat1, lon1, lat2, lon2);

Storage and forward function

Storage and forward function

waylayUtil.storeData(API_KEY, API_PASS, resource, data, domain, send)

With this function, you can automatically store and forward data (under the name resource).

API_KEY and API_PASS are tenant’s API credentials, domain is the tenant name. You need to keep the last argument send as it is. data is any JSON object. Resource is the unique name under which data will be stored and forwarded.

E-mail validation

returns true if the input entry is a valid email address.

var boolean =  waylayUtil.validateEmail("test@gmail.com");

Function node

The Function node operates on the raw data that is stored in the task context.

Function computation

If you have correctly created the metadata file of your sensor plugin, then the function editor will autocomplete the raw data that it can use for calculation. In case you have not defined the raw data as part of the sensor metadata, you will need to type everything manually. The screenshot below shows the autocompletion:

The function processing has a number of built-in capabilities that are described below:

Built-in functions

You can fetch the raw data from any node this way: <node.value1>. That allows you to do something like this: abs( <node.value1> - <node.value2>)

You can use all built in functions available in exp4j: Exp4j syntax

Built-in functions:

When raw data values are used in a function, arrows between their corresponding nodes and the function processing node will be auto-created by the waylay application:

Note that you should never try to connect sensors to the Function node, this is not going to work, just start typing the function in the Function node, that is all.

Using previous values

You can also fetch the data from previous measurements using [-n] syntax:

<node.value1> - <node.value1>[-1]

In the example above <node.value1>[-1] means: the value of the raw data parameter value1 at the previous invocation time.

Using time difference

You can also use a delta in time between measurements in your function with dt like this:

abs( <node.value1> - <node.value1>[-1]) / dt to get kind of first derivative computation

dt is replaced by time delta between invocations in seconds

Statistical computation

You can also use some extensions for statistics such as min, max, avg, std, count this way:

<max(node.value1)> - <min(node.value1)>

<count(5, node.pressure)> or like string search:

<count(Gent, node2.current_city)>

Aggregation types

You have 3 types of aggregation:

You can also mix different types in one function like this:

<max(3, minutes, node.value1)> - <min(5, samples, node2.value1)>

<max(3, samples, node.value1)> - <min(3, minutes, node2.value1)>

<count(5, 3, minutes, node.pressure)>

You can also count the number of times a node was in a given state, for instance, the number of time a node was in the state “OK” for the last three samples:

<count(OK, 3, samples, node.state)>

Note: limitation is that you can’t mix different aggregation types(samples and time) for the same parameter. But you can use overall aggregation (without samples, or time) and combine it with one of other aggregation types(samples or time).

Distance calculation

For any two nodes that return longitude and latitude via raw data, you can compute the distance using this function:


Distance is returned in km.


You can also search for a sequence of states, if you want to monitor for changes of node states in time.

<sequence([hello,world], node.state)>

Function above will either return 1 or 0. 1 indicates that a match of the sequence has been found.

For instance, if you want to know whether 3 nodes will be in “hello,world” sequence, you can create function this way:

<sequence([hello,world], node1.state)> + <sequence([hello,world], node2.state)> + <sequence([hello,world], node3.state)>

and test whether the result equals 3.

Stream data

In the waylay application, stream data is put in the GLOBAL context before sensor is executed (waylay takes care that that context is valid for each sensor). You can access it via in javascript: options.rawData.GLOBAL.<param> , nevertheless you should always use utility function as mentioned earlier to achieve the same stream data function:

var rawData = waylayUtil.getStreamData(options, "param");

In the Function node, you MUST omit options.rawData and use only this notation: <GLOBAL.param>

Before deploying a new task, you can always check in the debug window that computation is actually happening. In the example below we used a dice sensor and couple of Function nodes. Note that you can as well put one Function node on top of others: