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.
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.
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.
[
{
"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.
{
"type": "LogicAnd"
}
LogicOr
This condition is true if at least one of its neigbor conditions is true.
{
"type": "LogicOr"
}
LogicNot
This condition will negate next condition
{
"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).
{
"type": "DeviceParameter",
"id": 22,
"parameter": "Value",
"operation": ">",
"valueType": "Integer",
"value": 20
}
type
- is condition type identifierid
- device idparameter
- parameter name (see Butler API specification for more information on available parameters)operation
- comparison operation (see Scene Condition > Operations)valueType
- type of comparison value (see Scene Condition > Value Types)value
- comparison value itself (must be ommited if,valueType
equals"None"
)
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).
{
"type": "DeviceComparison",
"targets": [
{
"id": 22,
"parameter": "Level",
"valueType": "Integer"
},
{
"id": 23,
"parameter": "Value",
"valueType": "Double",
}
],
"operation": "<"
}
target
- (array of two elements) list of targetsid
- device idparameter
- parameter name (see Butler API specification for more information on available parameters)valueType
- type of comparison value (see Scene Condition > Value Types)
operation
- comparison operation Scene Condition > Operations
Weather Conditions
"type": "Weather"
If set up correctly, controller, periodically downloads weather data for configured location, changes of it can trigger scenes.
{
"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 humiditypressure
- pressuretime_of_day
-Night
orDay
, Night starts at the time of sunset, Day starts at sunriseweather
- weather conditions, available variants areRain
,Snow
,Mist
,Clear
,Clouds
. Theoperation
for it should becontains
operation
- comparison operation Scene Condition > OperationsvalueType
- 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 valuechanged
- value has changed (comparison value is ignored in this case)contains
- value contains comparison value (only forWeather
condition type)
For DevicesComparison
condition block there is also additional operation available:
diff==
- difference between values is equal to comparison valuediff!=
- difference between values is not equal to comparison valuediff>
- difference between values is greater than comparison valuediff>=
- difference between values is greater or equal to comparison valuediff<
- difference between values is less than comparison valuediff<=
- 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 neededInteger
- comparison value is integerInteger64
- comparison value is 64-bit integerDouble
- comparison value is double precision floating point numberFloat
- comparison value is single precision floating point numberString
- comparison value is stringBoolean
- comparison value is boolean
Scene Action
Scene action is the Lua code that will be executed when scene is triggered.
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.
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.
- block - current block index, see Scene Lifecycle
- gateway - gateway object, see Gateway Object
- log - logging functions, see Logging Functions
- json - JSON module, see JSON module
- http - HTTP module, see HTTP module
- utils - utility functions, see Utils module
Logging Functions
Logging functions are used to log messages to the system log. There are four levels of logging available: debug
, info
, warn
, error
.
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
log.debug("Debug message")
log.info("Info message")
log.warn("Warning message")
log.error("Error message")
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.
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.
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 clientsmessage
- push-notification message
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 humiditygateway.weather.temperature
- outside air temperature (in celsius)gateway.weather.pressure
- atmospheric pressuregateway.weather.visibility
- visibility in metersgateway.weather.time_of_day
-night
orday
, night starts at the time of sunset, day starts at sunrisegateway.weather.wind.speed
- wind speed in m/sgateway.weather.wind.direction
- wind direction in degreesgateway.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 areRain
,Snow
,Mist
,Clear
,Clouds
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.
gateway.scenes.activate(1)
gateway.scenes.deactivate(scene_id)
Deactivates scene by it's id.
Inactive scenes won't be triggered by conditions, but can be activated manually, or by other scenes.
gateway.scenes.deactivate(1)
gateway.scenes.run(scene_id)
Runs scene by it's id.
When scene is run this way, it's conditions are not checked, and it's action is executed immediately.
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 idname
- device nametype
- device type, see Butler API specification for more informationhidden
- if device is hidden in the UIlast_online
- timestamp of last device activityinterfaces
- 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 callparams
- table with parameters for the action
Calls device action with specified parameters.
Action won't be called if device doesn't support it, or if parameters are incorrect.
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 encodeoptions
- table with options for encodingpretty
- iftrue
, resulting JSON will be formatted with newlines and indentationindent
- integer, number of spaces to use for indentation, ignored ifpretty
isfalse
, default is2
local json_str = json.encode(
{
a = 1,
b = 2
})
-- will return '{"a":1,"b":2}'
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
local value = json.decode('{"a":1,"b":2}')
-- value will be a table {a = 1, b = 2}
HTTP module
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 URLoptions
- table with options for requestheaders
- table with headers to sendparams
- table with query parameters
Returns table with response data:
status
- HTTP status codebody
- response body
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 URLoptions
- table with options for requestheaders
- table with headers to sendparams
- table with query parametersbody
- request body
Returns table with response data:
status
- HTTP status codebody
- response body
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 formatHH:MM
period
- period to check, in formatHH:MM-HH:MM
Returns true
if time is in period, false
otherwise.
local result = utils.in_period("12:00", "10:00-14:00")
log.info("Time is in period: ", result)
-- will return true