12 KiB
Calculated Field TBEL Script Function
The calculate() function is a user-defined script that enables custom calculations using TBEL on telemetry and attribute data.
It receives arguments configured in the calculated field setup, along with an additional ctx object that provides access to all arguments.
Function Signature
function calculate(ctx, arg1, arg2, ...): object | object[]
Supported Arguments
There are three types of arguments supported in the calculated field configuration:
Attribute and Latest Telemetry Arguments
These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON.
Attribute and Latest telemetry are single value arguments that may be one of: boolean, int64 (long), double, string and JSON.
Example: Convert Temperature from Fahrenheit to Celsius
var temperatureC = (temperatureF - 32) / 1.8;
return {
"temperatureC": toFixed(temperatureC, 2)
}
Alternatively, using ctx to access the argument as an object:
{
"temperatureF": {
"ts": 1740644636669,
"value": 36.6
}
}
You may notice that the object includes both the value of an argument and its timestamp as ts.
Let's modify the function that converts Fahrenheit to Celsius to also return the timestamp information:
var temperatureC = (temperatureF - 32) / 1.8;
return {
"ts": ctx.args.temperatureF.ts,
"values": { "temperatureC": toFixed(temperatureC, 2) }
};
Time Series Rolling Arguments
These contain time series data within a defined time window. Example format:
{
"temperature": {
"timeWindow": {
"startTs": 1740643762896,
"endTs": 1740644662896
},
"values": [
{ "ts": 1740644350000, "value": 72.32 },
{ "ts": 1740644360000, "value": 72.86 },
{ "ts": 1740644370000, "value": 73.58 },
{ "ts": 1740644380000, "value": "NaN" }
]
}
}
The values are always converted to type double, and NaN is used when conversion fails. One may use isNaN(double): boolean function to check that the value is a valid number.
Example: Accessing time series rolling argument data
var startOfInterval = temperature.timeWindow.startTs;
var endOfInterval = temperature.timeWindow.endTs;
var firstItem = temperature.values[0];
var firstItemTs = firstItem.ts;
var firstItemValue = firstItem.value;
var sum = 0.0;
// iterate through all values and calculate the sum using foreach:
foreach(t: temperature) {
if(!isNaN(t.value)) { // check that the value is a valid number;
sum += t.value;
}
}
// iterate through all values and calculate the sum using for loop:
sum = 0.0;
for(var i = 0; i < temperature.values.size; i++) {
sum += temperature.values[i].value;
}
// use built-in function to calculate the sum
sum = t.sum();
Built-in Methods for Rolling Arguments
Time series rolling arguments support built-in functions for calculations. These functions accept an optional ignoreNaN boolean parameter.
| Method | Default Behavior (ignoreNaN = true) |
Alternative (ignoreNaN = false) |
|---|---|---|
max() |
Returns the highest value, ignoring NaN values. | Returns NaN if any NaN values exist. |
min() |
Returns the lowest value, ignoring NaN values. | Returns NaN if any NaN values exist. |
mean() |
Computes the average value, ignoring NaN values. | Returns NaN if any NaN values exist. |
std() |
Calculates the standard deviation, ignoring NaN. | Returns NaN if any NaN values exist. |
median() |
Returns the median value, ignoring NaN values. | Returns NaN if any NaN values exist. |
count() |
Counts values, ignoring NaN values. | Counts all values, including NaN. |
last() |
Returns the most recent value, skipping NaN values. | Returns the last value, even if it is NaN. |
first() |
Returns the oldest value, skipping NaN values. | Returns the first value, even if it is NaN. |
sum() |
Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. |
Usage example:
var avgTemp = temperature.mean(); // Returns 72.92
var tempMax = temperature.max(); // Returns 73.58
var valueCount = temperature.count(); // Returns 3
var avgTempNaN = temperature.mean(false); // Returns NaN
var tempMaxNaN = temperature.max(false); // Returns NaN
var valueCountNaN = temperature.count(false); // Returns 4
This function calculates air density using altitude (single value) and temperature (time series rolling argument).
function calculate(ctx, altitude, temperature) {
var avgTemperature = temperature.mean(); // Get average temperature
var temperatureK = (avgTemperature - 32) * (5 / 9) + 273.15; // Convert Fahrenheit to Kelvin
// Estimate air pressure based on altitude
var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588);
// Air density formula
var airDensity = pressure / (287.05 * temperatureK);
return {
"airDensity": airDensity
};
}
Merging Time Series Arguments
Time series rolling arguments can be merged to align timestamps across multiple datasets.
| Method | Description | Parameters | Returns |
|---|---|---|---|
merge(other, settings) |
Merges with another rolling argument. Aligns timestamps and filling missing values with the previous available value. |
|
Merged object with timeWindow and aligned values. |
mergeAll(others, settings) |
Merges with multiple rolling arguments. Aligns timestamps and filling missing values with the previous available value. |
|
Merged object with timeWindow and aligned values. |
Assuming the following arguments and their values:
{
"humidity": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
},
"values": [{
"ts": 1741356882759,
"value": 43
}, {
"ts": 1741356918779,
"value": 46
}]
},
"pressure": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
},
"values": [{
"ts": 1741357047945,
"value": 1023
}, {
"ts": 1741357056144,
"value": 1026
}, {
"ts": 1741357147391,
"value": 1025
}]
},
"temperature": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
},
"values": [{
"ts": 1741356874943,
"value": 76
}, {
"ts": 1741357063689,
"value": 77
}]
}
}
Usage:
var mergedData = temperature.merge(humidity, { ignoreNaN: false });
Output:
{
"mergedData": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
},
"values": [{
"ts": 1741356874943,
"values": [76.0, "NaN"]
}, {
"ts": 1741356882759,
"values": [76.0, 43.0]
}, {
"ts": 1741356918779,
"values": [76.0, 46.0]
}, {
"ts": 1741357063689,
"values": [77.0, 46.0]
}]
}
}
Usage:
var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true });
Output:
{
"mergedData": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
},
"values": [{
"ts": 1741357047945,
"values": [76.0, 46.0, 1023.0]
}, {
"ts": 1741357056144,
"values": [76.0, 46.0, 1026.0]
}, {
"ts": 1741357063689,
"values": [77.0, 46.0, 1026.0]
}, {
"ts": 1741357147391,
"values": [77.0, 46.0, 1025.0]
}]
}
}
Example: Freezer temperature analysis
This function merges temperature data with the fridge's defrost status. It then analyzes the merged data to identify instances where the fridge is not in defrost mode, yet the internal air temperature is too high ( > -5° C).
function calculate(ctx, temperature, defrost) {
var merged = temperature.merge(defrost);
var result = [];
foreach(item: merged) {
if (item.v1 > -5.0 && item.v2 == 0) {
result.add({
ts: item.ts,
values: {
issue: {
temperature: item.v1,
defrostState: false
}
}
});
}
}
return result;
}
The result is a list of issues that may be used to configure alarm rules:
[{
"ts": 1741613833843,
"values": {
"issue": {
"temperature": -3.12,
"defrostState": false
}
}
}, {
"ts": 1741613923848,
"values": {
"issue": {
"temperature": -4.16,
"defrostState": false
}
}
}]
Function return format
The return format depends on the output type configured in the calculated field settings (default: Time Series).
Time Series Output
The function must return a JSON object or array with or without a timestamp. Examples below return 5 data points: airDensity (double), humidity (integer), hvacEnabled (boolean), hvacState (string) and configuration (JSON):
Without timestamp:
{
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
}
}
With timestamp:
{
"ts": 1740644636669,
"values": {
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
}
}
}
Array containing multiple timestamps and different values of the airDensity :
[
{
"ts": 1740644636669,
"values": {
"airDensity": 1.06
}
},
{
"ts": 1740644636670,
"values": {
"airDensity": 1.07
}
}
]
Attribute Output
The function must return a JSON object without timestamp information. Example below return 5 data points: airDensity (double), humidity (integer), hvacEnabled (boolean), hvacState (string) and configuration (JSON):
{
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
}
}