nymea scripting

As of version 0.18 nymea supports scripting using JavaScript and QML. Scripting can be used to automate behaviors, similar to rules, but are aimed towards a more advanced usage of nymea. While scripting requires a basic understanding of the JavaScript programming language, it also features a much more powerful way to interact with nymea.


It is recommended to install nymea:app on a PC in order to use the script editor.


The recommended way to create and edit scripts is the integrated script editor in nymea:app. It can be found in the mains screen menu -> Magic using the script icon in the upper right corner.

deviceId completion


Introduction

nymea scripts follows the QML syntax. Please refer to the QML Documentation for details on the QML language. A script can be thought of like a small app running inside nymea core. As soon as a script has been deployed to nymea:core it will be loaded and execution starts. The script might then listen to state changes in the system, or use timer events to trigger execute actions in the system.


The nymea:app script editor will report console output for the script as well as errors in the script:

deviceId completion


Basic structure

An example of a simple nymea script looks like this

import QtQuick 2.0
import nymea 1.0

Item {

}

This example script will be functional, but it doesn't actually do anything. Depending on the purpose of the script, different content may be added inside the Item {} structure. Every script must have exactly one top level item, the so called root item. This root item might contain any number of child items.


For example, in order to act on a motion sensor detecting presence, such a block can be added:

import QtQuick 2.0
import nymea 1.0

Item {
    DeviceState {
        deviceId: "123456-1234-1234-123456"
        stateName: "isPresent"
        onValueChanged: {
            console.log("Presence detected:", value);
        }
    }
}

This script will log the text "Presence detected: true" into the scripts console when the motion sensor with the given deviceId detects a person and "Presence detected: false" when the person has left again.


IDs


deviceId

Throughout the nymea scripting, there will be the need to provide values for deviceId properties. Those must be set to a UUID which refers to a particular thing in the system.


The reason for using ids instead of device/thing names is because the id is persistent. The user might rename a thing and existing scripts will still continue work. If we'd use the device's name here, the scripts would be more fragile and break easily.


As those IDs are an implementation detail, they are not easily visible in the app, however, the nymea:app script editor provides code completion which allows to select things by name and it will automatically insert the proper UUID for it.


When placing the cursor after the text deviceId: " the script editor will pop up suggestions for all the devices in the system.


NOTE: Using CTRL+Space (or cmd+Space on macOS) allows to invoke the code auto completion at any point


deviceId completion



Selecting an entry will cause the script editor to fill in the appropriate UUID for this device/thing.


In this documentation we'll use <deviceId> as a placeholder for the actual id.

deviceId completion



stateName, eventNames, actionNames

Similar to deviceId, there are other properties which describe things in the system. Depending on the chosen deviceId the possible values for those differ. Please use the nymea:app code completion to get a list to chose from.


id

the id property is part of the QML language. Every element in QML can have an id property. The value for this can be defined freely by the script developer as long as it starts with a lowercase letter and does not contain special characters. Those ids basically give the element a name so that it can be referred to from other places in the script. It is not mandatory to add such id properties if it's not required to "wire" up elements.


Available types

The script engine supports all the types as found in the QML Documentation. In addition to those, a set of nymea specific types are supported which are the most important in this context.


DeviceState

A DeviceState is used to read or modify a certain device's state in the system.


A DeviceSate is required to have deviceId in order ot identify a thing. In addition to that, a stateName is required to identify a particular state. For example, a light bulb would have a stateName called power.


Each DeviceState has a value property which contains the actual current value of the state. For instance, a light bulb will contain true in the value property when it's turned on and false when it's off. Also it can be turned on or off by setting the value property accordingly.


In order to react on state changes, the onValueChanged signal can be connected.


Example:

DeviceState {
    deviceId: "<deviceId>"  // ID for the lightbulb
    stateName: "power"      // The name for the state we're interested in
    value: true             // This would initialize the value to `true`, turning the light on when nymea starts

    onValueChanged: {
        if (value == true) {
            console.log("the light has been switched on");
        } else {
            console.log("the light has been switched off");
        }
    }
}


DeviceEvent

A DeviceEvent is used to react on events in the system.


It requires a deviceId to select the device/thing we're watching for events and an eventName to select the actual event. For instance, a wall switch would have a pressed event.


DeviceEvents have a signal named triggered which is fired whenever the event happens. Using the onTriggered handler it is possible to act on those events.


The following example script would write a log warning whenever the switch is pressed.

DeviceEvent {
    deviceId: "<deviceId>"   // The id of the wall switch
    eventName: "pressed"     // The name of the event we're watching

    onTriggered: {
        console.log("Button pressed!")
    }
}


DeviceAction

A DeviceAction is used to execute an action on a particular device/thing.


DeviceActions require the use of deviceId to select a particular device/thing and an actionName to match the desired acton.


An action has a function called execute(params) which can be called to actually execute the action. The params are a map for the parameters this action requires.


For instance, a thing capable to send push notifications could to be used this way:

DeviceAction {
    id: notificationAction   // Giving it an id so we can call its execute() function
    deviceId: "<deviceId>"   // The id of the notification thing
    actionName: "notify"     // Selecting the "notify" action
}
...
notificationAction.execute({"title": "Hello", "body": "nymea rocks!"})


Alarm

An Alarm is used to execute actions or set states at given times


The alarm requires at least the property time to be set. In addition to that, endTime and weekDays may be set if desired.


The time property defines the time when this alarm should trigger. It is set in 24h format.


Whenever the alarm triggers it will emit the triggered signal. The onTriggered handler can be used to act on it.


endTime can be used to define a duration for the alarm. During the time between time and endTime, the active property will be set to true.

The weekDays property can be used to restrict the alarm to certain weekdays. If this property is not set, it will default to Alarm.AllDays causing the alarm to trigger every day at the specified time.

For instance, an alarm that triggers Monday to Friday at 7:00 in the morning can be set up with this example:

Alarm {
    time: "07:00"
    endTime: "08:00"
    weekDays: Alarm.Monday | Alarm.Tuesday | Alarm.Wednesday | Alarm.Thursday | Alarm.Friday

    onTriggered: {
        console.log("Alarm triggered!")
    }

    onActiveChanged: {
        if (active) {
            console.log("Alarm active now!")
        } else {
            console.log("Alarm ended")
        }
    }
}


Adding more JavaScript


Anywhere in the script, standard JavaScript can be added. For instance we can count the number of button presses. To execute an action when a button is pressed 5 times within 5 seconds, we can use such an example:

Item {
    id: root

    property int counter: 0

    Timer {
        id: timer
        interval: 5000
        repeat: false
        running: root.counter > 0 // The timer only runs when the counter is > 0
        onTriggered: {
            root.counter = 0; // Reset the counter after 5 secs
        }
    }

    DeviceEvent {
        deviecId: "<deviceId>" // id of button device
        eventName: "pressed"
        onTriggered: {
            root.counter = root.counter + 1;
            if (root.counter >= 5) {
                console.log("Button pressed 5 times within 5 seconds!")
            }
        }
    }
}


Putting things together


Binding states

DeviceStates can be linked using a property binding. For instance, to turn on a light while a presence sensor reports presence, set the light's power state value to the sensor's presence state:

DeviceState {
    id: presenseState
    deviceId: "<deviceId>" // id of presence sensor
    stateName: "isPresent"
}
DevieState {
    deviceId: "<deviceId>" // id of light device
    stateName: "power
    value: presenseState.value
}


In this example, the light's power state value is set to the presence sensor's isPresent state's value. This way, whenever the value for isPresent changes, it will automatically be synced to the power state.


Reacting on events

Devices that have events, e.g. a power wall switch that has a "pressed" event, can be used to execute an action in the system. Let's turn on a light when a button is pressed, but only if it's dark outside:

DeviceEvent {
    deviceId: "<deviecId>" // id of button device
    eventName: "pressed"
    onTriggered: {
        if (daylightState.value == false) {
            lightPowerState.value = true;
        }
    }
}
DeviceState {
    id: daylightState
    deviceId: "<deviceId>" // id of daylight sensor
    stateName: "daylight"
}
DeviceState {
    id: lightPowerState
    deviceId: "<deviceId>" // id of light
    stateName: "power"
}