Overview Script programming

With this function block you can program your own scripts in a high-level language. Lua (www.lua.org) is used as the interpreter. This description only covers the language extensions.

Inputs

E1-x
Input 1-x
Inputs. These can be accessed in the script via the global variables E1, E2, etc. For a better overview, alias names can also be assigned to them.



Outputs

A1-Ax
Output 1-x
Outputs. These can be accessed in the script via the global variables A1, A2, etc. For a better overview, alias names can also be assigned to them.



Parameters

Number of inputs
Number of inputs (1 to 64)
Number of outputs
Number of outputs (1 to 64)
onEvent Filter
Selection "Active (only connected)" or "Inactive (all telegrams)". The filter only affects the callback function onEvent: With "Active" (default), only telegrams whose address is connected at any input or output of this block are forwarded to the script. With "Inactive", all telegrams are forwarded, additionally also READ requests. The filter has no effect on the functions sys_get_value and sys_set_value; these also work without a connection. Please note that processing all events in large projects takes more processing time, so the filter should only be deactivated when this is necessary.
Password Optionally, a password can be assigned. The script can then only be edited with a valid password. Please note: There is no way to reset the password.
Edit
Click this button to edit the script; alternatively you can also right-click on the function block.


Editing window

Clicking the Edit parameter or right-clicking on the function block displays the dialog for editing the script:



This dialog is divided into the following three areas:

  1. Script: Here the script is edited.
  2. Console: This window displays all output from the interpreter, for example info and error messages or messages that are output via the function call "sys_debug_print".
  3. Variables: This window displays the value of internal variables. Unlike the Console window, the output is not continuous but in a table. Which variables are displayed here is defined with the function call "sys_debug_watch".
Important: In order for messages or variables to be output, the Play button must be pressed. Clicking Save transfers the script to the controller and executes it immediately, but only if the Play button is pressed:




By default, there is only the function "onInputChanged()" in the script. It is called at startup and whenever something at the inputs has changed. For most applications this is also sufficient. Via the menu button, further system functions can be added. These so-called callback functions are described below:

Important:


onCreate()
This function is called once when the function block is initialized. Here, for example, initializations of global variables can be defined.
onInputChanged()
This function is called when the initialization is finished (after onCreate), or the value at a connected input has changed. Telegrams that send the same value again do not trigger a call — if the script needs to react to every telegram, the function onEvent must be used.
onEvent(Value,PhysAddress,GroupAddress,Command)
This function is called when a telegram has been received, for example a KNX variable connected at the input. By default (parameter "onEvent Filter" = Active), only telegrams whose address is connected at an input or output of this block are forwarded to the script — at which input/output and in which order the address is connected does not matter. With "Inactive (all telegrams)", all telegrams are forwarded. READ requests are only forwarded when the filter is inactive.

The following arguments are passed to the function:

  • Value: Value. Type depends on the KNX data type of the address — usually Number (e.g. DPT1 as 0/1, DPT5 as 0..255, DPT9 as Float), for string data types (DPT15) as String.
  • PhysAddress: Source address in the format "0.0.1"
  • GroupAddress: Destination address in the format "0/0/1"
  • Command: Command as a string, one of "WRITE", "RESPONSE", "READ".
Important: This function should only be used when it is really necessary to react to telegrams, for example KNX telegrams. If possible, the function onInputChanged should be used. When onEvent is activated, the telegrams are forwarded to the interpreter, which results in a higher load on the controller in large projects.

onTimerEvent100ms() This function is called every 100 ms
onTimerEvent1s()
This function is called once per second.
onTimerEvent10s()
This function is called every 10 seconds.
onTimerEvent30s()
This function is called every 30 seconds.
onTimerEvent1m()
This function is called once per minute. The call always occurs at the beginning of every minute.
onTimerEvent1h()
This function is called once per hour. The call always occurs at the beginning of every hour.
onGlobalChanged(Id,Value) This function is called when a global variable has been changed via sys_set_global_value.
onOsExecuteFinished(Id,Result) This function is called when the command called via sys_os_execute has been executed. Id is the Id that was passed by sys_os_execute, Result is the result, 0=No error, otherwise an error code depending on the command that was called.
onNetRead(DataString,SizeString,DataHex,SizeHex) This function is called when data has been received via a network connection. The data is passed both as a string and a hexadecimal string.
onNetState( State, Message ) This function is called when the network status changes:

  • -1: _STATE_CREATE_ERROR
  • -2: _STATE_CONNECT_ERROR
  • -3: _STATE_ERROR_READ
  • 2: _STATE_CONNECT_OK
onHttpResponse( Id, Status, Body, Headers ) This function is called when the response to a sys_http_request is available. Id is the request id returned by sys_http_request, Status is the HTTP status code (e.g. 200) or a negative value on transport/SSL error (-2 = HTTPS without SSL support), Body is the response text and Headers a Lua table of the response headers.
onProcess() Called on every runtime cycle. Important: use only for time-critical calculations — the call occurs very frequently and can load the entire controller with complex logic.
onMqtt( Topic, Message ) Called when an MQTT message is received. Topic = topic as a string, Message = content as a string. This requires a configured MQTT client in the settings of the controller.


Connections and live testing

Frequent questions about the interaction of script, connections and telegram filter:

Whether telegrams arrive or are filtered can be checked directly in the editing window (Play button pressed, then save — the script runs immediately on the controller):

function onEvent(Value, PhysAddress, GroupAddress, Command)
  sys_debug_print(Command, " ", GroupAddress, " = ", Value, " from ", PhysAddress)
end

Every telegram that reaches the block is displayed in the console this way. If no messages appear although telegrams are expected, they are being filtered while "onEvent Filter" is active (address not connected) — as a check, temporarily set the filter to "Inactive".


System calls


The following system calls are currently integrated.



sys_set_value("0/0/0",Value,Force)
Writing any variable:

The variable is only sent when the value has changed. This means the function can also be called cyclically.

  • Address: Destination address as a string "0/0/0".
  • Value: Value.
  • Force: If 1, the variable is always sent, even if the value has not changed; if 0, only when the value has changed. Mandatory parameter — when called with only 2 arguments, the function is silently ignored.
sys_get_value("0/0/0")
Query the value of a variable

  • The address is passed as an argument in the form "0/0/0".

sys_debug_print(...)
Output of a line in the console. On every call a new line is output in the debug console; this can be used, for example, to check whether a function has been called.

The function accepts any number of arguments, all of which are output concatenated as a string. Numbers are automatically converted to strings, strings must be in quotation marks.

Examples:
  • sys_debug_print("Hello")
  • sys_debug_print("x=", x)
  • sys_debug_print("E1=", E1, " A1=", A1)

sys_debug_watch("Name: ",Value)
Output the value of a variable.

Two arguments are passed to the function:

  • Name: Unique designation
  • Value: Any value (texts must be placed in quotation marks)
All calls of e.g. "sys_debug_watch("V1",Value)" are output in a separate line in the variables window. This can be used to monitor the value of a variable. Please note that the variable in the variables window is only updated when this function is called.

sys_diag_message("Message",Type,Level) Output a message or error in the diagnostics in the Studio. The message is output directly in the diagnostics area (footer - message/error). This makes diagnostics easier when additional operation via the visualization is necessary.

All three arguments are mandatory. Order: first Message, then Type, then Level — if the order is swapped, the message is silently ignored.
  • Message: Text that is output.
  • Type: 0=message, 1=error. Other values are treated as a message.
  • Level: 0=standard, 1=output message only when extended diagnostics is active (for support only).
sys_alias("E1","Value_xy")
With this function, a symbolic name can be assigned to an input or output. These are then also displayed in the window for the connections.

Important: Aliases should be defined in the function "onCreate()" (technically the call is also permitted in other callbacks, but the alias only takes effect from the next callback entry). The alias names must not contain spaces, umlauts or special characters. If an alias name has been assigned to an output, the alias takes precedence over A1, etc.: an assignment to A1 is NOT transferred to the output after the callback; instead the value of the alias variable is used.

  • sys_alias("E1","OutsideTemperature"): Assigns the first input the name "OutsideTemperature", which can then be addressed in the following script as "OutsideTemperature". For example if ( OutsideTemperature > 10 ).
  • sys_alias("A1","Setpoint"): Assigns output A1 the name "Setpoint", which can then be addressed in the following script as "Setpoint". For example "Setpoint = 10".

sys_get_addr_in(1)
Returns the address of the connected variable of an input in the format "1/2/3"; here 1 is E1, etc.
sys_get_addr_out(1) Returns the address of the connected variable of an output in the format "1/2/3"; here 1 is A1, etc.


sys_set_persistent_value("Id","Value")
With this function, any data can be stored in the persistent area of the controller. Unlike the function sys_set_value, this function can store any values regardless of the data type of a variable.

Important: The controller is equipped with a flash memory that must not be written any number of times. This function must therefore not be called too often. Constantly saving persistent values shortens the lifetime of the flash memory and leads to a defect or data loss.
sys_get_persistent_value("Id","DefaultValue") With this function, the persistent values can be read back.
sys_set_global_value("Id","Value",force) Set a global variable. With this function, global variables can be used. Via these, multiple instances of the  interpreter can communicate. For example, multiple script function blocks without having to use variables.

A unique Id and the value are passed as parameters, optionally the parameter force ( 1 or 0 ).

When a value is changed, the callback function "onGlobalChanged" is called on all function blocks, except the block that changed the value. If the parameter force is specified with 1, the function "onGlobalChanged" is always called, even if the value has not changed.
sys_get_global_value("Id","DefaultValue") Read the global variable.
ms = sys_get_tick_count()
Returns the time elapsed since the start of the controller in milliseconds as an integer. Useful for your own time measurements or for calculating differences.
sys_force_update_output(index)
Forces an output to be sent again to the connected bus. index = 1-based output number (1 = A1, 2 = A2, etc.). Useful when the current value should be output again without a change (e.g. cyclic refresh telegrams).
pidId = sys_pid_create()
Creates a PID controller and returns its ID.
output = sys_pid_call(pidId, input, p, i, d)
Calculates a PID step. input = current actual value, p/i/d = controller parameters. It is typically called cyclically (e.g. from onTimerEvent1s) with the current actual value and returns the manipulated value.


id = sys_os_execute("command") Call an external program. This call is similar to the Lua command "os.execute", however the command is executed in the background and does not block the script. After execution, the callback function onOsExecuteFinished is called.

Argument: complete shell command as a string.

Return: a consecutive execution ID (integer), NOT the exit code. The exit code comes asynchronously as the second parameter "Result" in onOsExecuteFinished(Id, Result), where Id matches this return value.

Important: For slow commands, set a timeout in the command itself (e.g. "curl --max-time 5 ..."), as the background thread would otherwise keep running for a long time.
sys_send_mail(To, Subject, Body, Attachment) Send an email. All four parameters are mandatory (for "no attachment" pass an empty string "").

  1. To: Recipient. Multiple recipients can be specified separated by a semicolon.
  2. Subject: Subject.
  3. Body: Content.
  4. Attachment: Attachment as a complete file path. Multiple file names can be specified separated by a semicolon. Empty string "" for no attachment.
Return: 1 on success, -1 on error (e.g. empty recipient).
sys_send_pushover(User, Title, Message, Priority, Sound, Device, Url, UrlTitle, ApiToken) Send a push message via Pushover without having to create a dedicated function block. The first three parameters are mandatory, all others may be omitted.

  1. User: User or group key. Multiple keys can be specified separated by a space, semicolon or comma; one message is sent per key.
  2. Title: Heading of the message.
  3. Message: Text of the message.
  4. Priority: -2 to 2. 0 = normal, 1 = high, 2 = emergency (the message is repeated until it is acknowledged).
  5. Sound: Name of the sound, e.g. "siren" or "cosmic". Empty = the recipient's default sound.
  6. Device: Name of a single device. Empty = all devices of the user.
  7. Url: Address shown as a link in the message.
  8. UrlTitle: Caption of this link.
  9. ApiToken: Own application token. Empty = token of uni-PRO.
Return: 1 if the message was accepted, 0 if the permitted number of messages per hour or day has been reached, -1 on a parameter error. The return value says nothing about the actual delivery.

Important: The number of messages per hour and day is limited. This limit is shared with all Pushover function blocks of the project.

Example:
  sys_send_pushover("uQiRzpo4DXghDmr9QzzfQu27cmVRsG","Alarm","Cistern empty",1,"siren")
sys_send_ntfy(Server, Topic, Title, Message, Priority, Tags, Click, Attach, Token) Send a push message via ntfy without having to create a dedicated function block. The first four parameters are mandatory, all others may be omitted.

  1. Server: Address of the ntfy server, optionally with protocol and port number, e.g. "https://ntfy.example.com:8443". Empty = "ntfy.sh" over an encrypted connection.
  2. Topic: Topic the message is published under. The topic is used unchanged.
  3. Title: Heading of the message.
  4. Message: Text of the message.
  5. Priority: 1 to 5, 3 = default.
  6. Tags: Tags separated by a comma, e.g. "warning,droplet". ntfy displays known tags as a symbol.
  7. Click: Address that is opened when the message is tapped.
  8. Attach: Address of a file that is attached to the message.
  9. Token: Access token of the ntfy account. Empty = without authentication.
Return: 1 if the message was accepted, 0 if the permitted number of messages per hour or day has been reached, -1 on a parameter error. The return value says nothing about the actual delivery.

Important: Without an access token anyone who knows the topic can read the messages. For anything other than test messages use a topic that is hard to guess or your own server with a token. The number of messages per hour and day is limited; this limit is shared with all NTFY function blocks of the project.

Example:
  sys_send_ntfy("","house-alarm-3f7a2b","Alarm","Cistern empty",4,"warning,droplet")

Note on the timer mechanism: The sys_timer_* functions are passive poll timers — there is NO callback on expiry. The status must be queried in a regular onTimerEvent* callback itself with sys_timer_finished(id). For fixed intervals (100ms, 1s, 10s, 30s, 1m, 1h) the onTimerEvent* callbacks without sys_timer_create are simpler.
id = sys_timer_create() Creates a timer. Before timers can be used, they must first be initialized. This function returns an ID, which is used as a parameter for all further functions.
sys_timer_start(id,timeMs) Starts the countdown of the timer (runtime in milliseconds). Triggers NO callback — the expiry must be actively queried via sys_timer_finished.
sys_timer_stop(id) Stops the timer.
sys_timer_elapsed(id) Returns the time since the start of the timer in milliseconds.
sys_timer_finished(id) Returns 1 when the time specified with "sys_timer_start" has elapsed, otherwise 0. This function is the only way to find out that the timer has expired — typically queried in onTimerEvent100ms().

Example:
local t = -1
function onCreate()
  t = sys_timer_create()
  sys_timer_start(t, 500)
end
function onTimerEvent100ms()
  if sys_timer_finished(t) == 1 then
    A1 = 1 - A1
    sys_timer_start(t, 500)  -- restart
  end
end



sys_comm_open("/dev/ttyONBOARD-232","115200","8","1","N") With this function, the serial interface is initialized.

  1. Name of the interface. /dev/ttyONBOARD-232 = RS232, /dev/ttyONBOARD-485 = RS485
  2. Baud rate
  3. Data bits, 7 or 8
  4. Stop bits, 1 or 2
  5. Parity, N=none, E=even, O=odd
Returns 1 if successful, 0 on error.
sys_comm_close() The port is closed.
len,dataString = sys_comm_read(len,timeout,"_ASCII") Reads data from the interface

  1. Number of bytes to be read
  2. Timeout in milliseconds
  3. Format. _ASCII as a string, _HEX in hexadecimal notation (for binary data).
Return

  1. len = number of data bytes
  2. dataString = data as a string
Important: This function blocks until the number of bytes has been received or the timeout occurs.
sys_comm_write(len,data,"_ASCII") Sends data to the interface

  1. Length of the data output at the interface. Normally this is the length of the string. If _HEX is used, this is half, since two characters represent one data byte.
  2. Data
  3. Format, _ASCII as a string, _HEX in hexadecimal notation (for binary data).
Return

  1. Number of bytes sent



Id = sys_net_create("_TCP",IPAddress,IPPort,Timeout) Initializes the network connection. This function is not blocking. The connection is established in the background; the status is returned in the callback function "onNetState".
  1. Type. "_TCP" or "_UDP" (UDP still in preparation)
  2. IP address
  3. Port number
  4. Timeout in milliseconds.
Return

  1. ID of the connection. Currently only one connection per script block is possible. -1 on error (for example if the connection is already established).


See callback functions "onNetState( State, Message )", "onNetRead( DataString, SizeString, DataHex, SizeHex )"

sys_net_write(Data,"_STRING") Sends data to the connected network device

  1. Data. Data to be sent
  2. Format, _ASCII as a string, _HEX in hexadecimal notation (for binary data).

Return

  1. -1 on error, otherwise the length of the data sent.
sys_net_close(Id) Closes the connection.



Id = sys_http_request(Method,Url,Body,ContentType,Headers) Sends an HTTP or HTTPS request. This function is not blocking: the request is processed in the background, the response is returned in the callback function "onHttpResponse".

  1. Method. "GET", "POST", "PUT" or "DELETE"
  2. Url. Full address with http:// or https:// (HTTPS only on controllers with SSL support)
  3. Body. Content to send (empty string "" for GET)
  4. ContentType. e.g. "application/json" (empty for default)
  5. Headers. Optional Lua table with additional header lines, e.g. { ["Authorization"]="Bearer ..." }
Return

  1. Request id. This id is passed to the callback function "onHttpResponse". -1 on an invalid call.


The response arrives asynchronously via the callback function "onHttpResponse( Id, Status, Body, Headers )". Comparing the return value directly with the HTTP status is wrong – the function returns the request id, not the status.

Example:

function onCreate()
  sys_http_request("GET","https://host/api/status","","",{})
end

function onHttpResponse( Id, Status, Body, Headers )
  sys_debug_print("HTTP "..Status.." "..Body)
end




See also common parameters of all function blocks.