Skip to main content

Lua Scenarios

About

Scenario system in Butler controller is used to automatize behaviour of your home.

Lua scenarios are created to extend automatization ability even more, with the possibility to implement more advanced automatization scenarios (then is possible using basic block scenes) in Lua Programming Language.

Scene consists of condition and action parts, which are edited in respective and separate UI fields.

Scene Lifecycle

When scene is created, scene manager collects the list of possible triggers for it, and stores them.

Then, when any of the triggers is activated (e.g. triggering parameter of the device changes), scene manager check condition of the scene, and then calls action part of the scene.

Scene execution is divided into blocks, each block is executed sequentially, and can return a delay before next block, and (optionally) a message to be logged.

warning

Context of the scene is discarded after each block , so if you need to store some data between blocks, you should use global variables. Sharing data between block is not implemented yet.

Execution of scene stop when any block returns finished = true, or when all blocks are executed.

note

There is also hard limit of block counters, if scene exceeds it, it will be stopped.

Scene Condition

Scene condition is defined as a JSON object.

Example of scene conditions
[
{
"type": "DeviceParameter",
"id": 22,
"parameter": "Level",
"operation": "==",
"valueType": "Integer",
"value": 20
},
{
"type": "LogicOr"
},
{
"type": "DeviceParameter",
"id": 23,
"parameter": "currentSetpoints.Heat",
"operation": "changed",
"valueType": "None"
}
]

Root of the scene condition is an array containing descriptions of conditions. Condition are evaluated sequentially.

Condition Types

For each condition, in condition array, field type is mandatory, and specifies what condition type is. Condition fields can be different for different condition types (except for type field), all of them are described below.

Logic Conditions

Logic conditions are used to combine multiple conditions together or negate them to create more complex scenarios. There are three types of logic conditions available:

LogicAnd

This condition is true if conditions before and after are both true.

Example
{
"type": "LogicAnd"
}
LogicOr

This condition is true if at least one of its neigbor conditions is true.

Example
{
"type": "LogicOr"
}
LogicNot

This condition will negate next condition

Example
{
"type": "LogicNot"
}

Device Conditions

There are several condition types, that are checking device parameters.

Device Parameter Change

"type": "DeviceParameter"

Scene can be triggered on the change of "any" parameter of any device, if said change makes condition truethful (changed operation is a bit of exception, see Scene Condition > Operations > changed).

Example to trigger when temperature of room is higher than 20
{
"type": "DeviceParameter",
"id": 22,
"parameter": "Value",
"operation": ">",
"valueType": "Integer",
"value": 20
}
Devices Comparison

"type": "DevicesComparison"

This condition type is used to trigger when result of comparison of parameter values of two devices becomes true. User should specify two targets for the comparison, which can be any arbitrary parameter of any device, the only requirement is, that parameter are both of the same type (i.e both are numeric).

Example
{
"type": "DeviceComparison",
"targets": [
{
"id": 22,
"parameter": "Level",
"valueType": "Integer"
},
{
"id": 23,
"parameter": "Value",
"valueType": "Double",
}
],
"operation": "<"
}

Weather Conditions

"type": "Weather"

If set up correctly, controller, periodically downloads weather data for configured location, changes of it can trigger scenes.

Example that triggers scene when it's day time
{
"type": "Weather",
"condition": "time_of_day",
"operation": "==",
"valueType": "String",
"value": "Day"
}
  • condition - weather property to check Can be one of following variants:
    • temperature - outside air temperature (in celsius)
    • humidity - relative humidity
    • pressure - pressure
    • time_of_day - Night or Day, Night starts at the time of sunset, Day starts at sunrise
    • weather - weather conditions, available variants are Rain, Snow, Mist, Clear, Clouds. The operation for it should be contains
  • operation - comparison operation Scene Condition > Operations
  • valueType - type of comparison value (see Scene Condition > Value Types)
  • value - comparison value itself (must be ommited if, valueType equals "None")

Operations

Operations are used to compare values in conditions.

  • == - value is equal to comparison value
  • != - value is not equal to comparison value
  • > - value is greater than comparison value
  • >= - value is greater or equal to comparison value
  • < - value is less than comparison value
  • <= - value is less or equal to comparison value
  • changed - value has changed (comparison value is ignored in this case)
  • contains - value contains comparison value (only for Weather condition type)

For DevicesComparison condition block there is also additional operation available:

  • diff== - difference between values is equal to comparison value
  • diff!= - difference between values is not equal to comparison value
  • diff> - difference between values is greater than comparison value
  • diff>= - difference between values is greater or equal to comparison value
  • diff< - difference between values is less than comparison value
  • diff<= - difference between values is less or equal to comparison value

Value Types

Value type specifies how comparison value should be treated.

  • None - no comparison value is needed
  • Integer - comparison value is integer
  • Integer64 - comparison value is 64-bit integer
  • Double - comparison value is double precision floating point number
  • Float - comparison value is single precision floating point number
  • String - comparison value is string
  • Boolean - comparison value is boolean

Scene Action

Scene action is the Lua code that will be executed when scene is triggered.

Simple example of scene action
local device = gateway.get_device(33)

device:call_action("setStatus", {status = true})

return {
status = true,
delay = 0,
message = "Device turned on",
finished = true
}

Scene should return a result of it's execution in a form of a table with following fields:

  • status - result of scene execution (boolean)
  • delay - delay before next scene step (integer) see Scene Action > Delay
  • message - message to be logged in the system (string)
  • finished - if scene is finished or not (boolean)

Simplified return statement can also be used, in this case, it should be an array of 4 elements, in the following order: status, delay, message, finished.

Simplified return statement
return {true, 0, "Device turned on", true}

Globals

Butler's Lua runtime provides a set of functions and objects to interact with the system, as well as some additional utilities.

Logging Functions

Logging functions are used to log messages to the system log. There are four levels of logging available: debug, info, warn, error.

info

All logging functions accept multiple arguments of any type, which will be concatenated into a single message.

log.debug(...)

logs message with debug level

log.info(...)

logs message with info level

log.warn(...)

logs message with warning level

log.error(...)

logs message with error level

Example of logging functions
log.debug("Debug message")
log.info("Info message")
log.warn("Warning message")
log.error("Error message")
Logging function supports any number of arguments of any type
log.debug("Debug message", {a = 1, b = 2}, 3, true)

Gateway

gateway object is used to interact with the system, get information about devices, rooms, etc.

gateway.get_device(device_id)

Returns device object by it's id.

Example of getting device object
local device = gateway.get_device(33)

Resulting object represents device, for full list of available methods see Device Object

gateway.get_devices()

Returns list of all devices in the system.

Example of getting all devices
local devices = gateway.get_devices()

for _, device in ipairs(devices) do
log.info("Device name: ", device.name)
end

gateway.send_notification(clients, message)

Send push-notification to specified clients.

Parameters:

  • clients - list of client ids to send notification to, or * to send to all clients
  • message - push-notification message
Example of sending notification
local device = gateway.get_device(33)
gateway.send_notification({"*"}, "Device " .. device.name .. " turned " .. (device.parameters.Status == true and "on" or "off")

gateway.weather

Contains current weather data

  • gateway.weather.humidity - relative humidity
  • gateway.weather.temperature - outside air temperature (in celsius)
  • gateway.weather.pressure - atmospheric pressure
  • gateway.weather.visibility - visibility in meters
  • gateway.weather.time_of_day - night or day, night starts at the time of sunset, day starts at sunrise
  • gateway.weather.wind.speed - wind speed in m/s
  • gateway.weather.wind.direction - wind direction in degrees
  • gateway.weather.wind.cardinal - wind direction in cardinal points (N, NE, E, SE, S, SW, W, NW)
  • gateway.weather.weather - list of current weather conditions, available variants are Rain, Snow, Mist, Clear, Clouds
Example of using weather data
local device = gateway.get_device(33)

if gateway.weather.temperature > 20 then
device:call_action("setStatus", {status = true})
else
device:call_action("setStatus", {status = false})
end

gateway.scenes

Contains methods to interact with other scenes

gateway.scenes.activate(scene_id)

Activates scene by it's id.

Example of activating scene
gateway.scenes.activate(1)
gateway.scenes.deactivate(scene_id)

Deactivates scene by it's id.

info

Inactive scenes won't be triggered by conditions, but can be activated manually, or by other scenes.

Example of deactivating scene
gateway.scenes.deactivate(1)
gateway.scenes.run(scene_id)

Runs scene by it's id.

info

When scene is run this way, it's conditions are not checked, and it's action is executed immediately.

Example of running scene
gateway.scenes.run(1)

Device Object

Instance of device object is returned by gateway.get_device function. It is used to interact with the device, get information about it, and call actions.

Fields:

  • id - device id
  • name - device name
  • type - device type, see Butler API specification for more information
  • hidden - if device is hidden in the UI
  • last_online - timestamp of last device activity
  • interfaces - list of device interfaces (e.g. ZWaveDevice, SwitchBinary, etc. refer to Butler API specification for more information on interfaces and their parameters)
  • parameters - table containing device parameters (e.g. Level, Value, etc. refer to Butler API specification for more information on parameters and their values

device:call_action(action_name, params)

Parameters:

  • action_name - name of the action to call
  • params - table with parameters for the action

Calls device action with specified parameters.

info

Action won't be called if device doesn't support it, or if parameters are incorrect.

Example of calling device action
local device = gateway.get_device(33)
device:call_action("setStatus", {status = true})

JSON module

Provides functions to encode and decode JSON strings.

json.encode(value, options)

Returns JSON string representation of the value.

Parameters:

  • value - value to encode
  • options - table with options for encoding
    • pretty - if true, resulting JSON will be formatted with newlines and indentation
    • indent - integer, number of spaces to use for indentation, ignored if pretty is false, default is 2
Example of encoding JSON
local json_str = json.encode(
{
a = 1,
b = 2
})
-- will return '{"a":1,"b":2}'
Example of encoding JSON with pretty formatting
local json_str = json.encode(
{
a = 1,
b = 2
},
{
pretty = true,
indent = 8
})
--[[
will return:
{
"a": 1,
"b": 2
}
]]

json.decode(json_str)

Returns decoded value from JSON string.

Parameters:

  • json_str - JSON string to decode
Example of decoding JSON
local value = json.decode('{"a":1,"b":2}')
-- value will be a table {a = 1, b = 2}

HTTP module

warning

HTTP module is synchronous, and will block execution until request is finished, therefore it can (and probably will) slow down scene execution.

Provides functions to make HTTP requests.

http.get(url, options)

Makes GET request to specified URL.

Parameters:

  • url - request URL
  • options - table with options for request
    • headers - table with headers to send
    • params - table with query parameters

Returns table with response data:

  • status - HTTP status code
  • body - response body
Example of making GET request
local response = http.get("http://example.com")
log.info("Response status: ", response.status)
log.info("Response body: ", response.body)

http.post(url, options)

Makes POST request to specified URL.

Parameters:

  • url - request URL
  • options - table with options for request
    • headers - table with headers to send
    • params - table with query parameters
    • body - request body

Returns table with response data:

  • status - HTTP status code
  • body - response body
Example of making POST request
local response = http.post("http://example.com", {
body = json.encode({
a = 1,
b = 2
}),
headers = {
["Content-Type"] = "application/json"
}
})
log.info("Response status: ", response.status)
log.info("Response body: ", response.body)

http.put(url, options)

Makes PUT request to specified URL.

Same parameters and return value as http.post(url, options)

http.delete(url, options)

Makes DELETE request to specified URL.

Same parameters and return value as http.post(url, options)

Utils module

Provides utility functions.

utils.in_period(time, period)

Checks if specified time is in specified period.

Parameters:

  • time - time to check, in format HH:MM
  • period - period to check, in format HH:MM-HH:MM

Returns true if time is in period, false otherwise.

Example of checking if time is in period
local result = utils.in_period("12:00", "10:00-14:00")
log.info("Time is in period: ", result)
-- will return true