2025-03-10 16:09:42 +02:00
## Calculated Field TBEL Script Function
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
The **calculate()** function is a user-defined script that enables custom calculations using [TBEL ](\${siteBaseUrl}/docs\${docPlatformPrefix}/user-guide/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.
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
### Function Signature
2025-03-03 11:04:10 +02:00
```javascript
2025-03-10 16:09:42 +02:00
function calculate(ctx, arg1, arg2, ...): object | object[]
2025-03-03 11:04:10 +02:00
```
2025-03-10 16:09:42 +02:00
### Supported Arguments
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
There are three types of arguments supported in the calculated field configuration:
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
#### Attribute and Latest Telemetry Arguments
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
These arguments are single values and may be of type: boolean, int64 (long), double, string, or JSON.
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
Attribute and Latest telemetry are single value arguments that may be one of: boolean, int64 (long), double, string and JSON.
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
**Example: Convert Temperature from Fahrenheit to Celsius**
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```javascript
var temperatureC = (temperatureF - 32) / 1.8;
return {
"temperatureC": toFixed(temperatureC, 2)
}
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
Alternatively, using `ctx` to access the argument as an object:
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"temperatureF": {
"ts": 1740644636669,
"value": 36.6
2025-03-03 11:04:10 +02:00
}
2025-03-10 16:09:42 +02:00
}
```
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:
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
```javascript
var temperatureC = (temperatureF - 32) / 1.8;
return {
"ts": ctx.args.temperatureF.ts,
"values": { "temperatureC": toFixed(temperatureC, 2) }
};
```
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
#### Time Series Rolling Arguments
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
These contain time series data within a defined time window. Example format:
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"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" }
]
2025-03-10 10:42:46 +02:00
}
2025-03-10 16:09:42 +02:00
}
```
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
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.
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
**Example: Accessing time series rolling argument data**
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```javascript
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();
```
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
##### Built-in Methods for Rolling Arguments
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
Time series rolling arguments support built-in functions for calculations. These functions accept an optional `ignoreNaN` boolean parameter.
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
| 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. |
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
Usage example:
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```javascript
var avgTemp = temperature.mean(); // Returns 72.92
var tempMax = temperature.max(); // Returns 73.58
var valueCount = temperature.count(); // Returns 3
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
var avgTempNaN = temperature.mean(false); // Returns NaN
var tempMaxNaN = temperature.max(false); // Returns NaN
var valueCountNaN = temperature.count(false); // Returns 4
```
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
This function calculates air density using `altitude` (single value) and `temperature` (time series rolling argument).
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```javascript
function calculate(ctx, altitude, temperature) {
var avgTemperature = temperature.mean(); // Get average temperature
var temperatureK = (avgTemperature - 32) * (5 / 9) + 273.15; // Convert Fahrenheit to Kelvin
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
// Estimate air pressure based on altitude
var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588);
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
// Air density formula
var airDensity = pressure / (287.05 * temperatureK);
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
return {
"airDensity": airDensity
};
}
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
##### 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. | < ul >< li > `other` (another rolling argument)</ li >< li > `settings` (optional) - configuration object, supports:< ul >< br />< li > `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.</ li >< li > `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.</ li ></ ul ></ li ></ ul > | 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. | < ul >< li > `others` (array of rolling arguments)</ li >< li > `settings` (optional) - configuration object, supports:< ul >< br />< li > `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.</ li >< li > `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.</ li ></ ul ></ li ></ ul > | Merged object with timeWindow and aligned values.|
Assuming the following arguments and their values:
```json
{
"humidity": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
2025-03-07 17:53:27 +02:00
},
2025-03-10 16:09:42 +02:00
"values": [{
"ts": 1741356882759,
"value": 43
}, {
"ts": 1741356918779,
"value": 46
}]
},
"pressure": {
"timeWindow": {
"startTs": 1741356332086,
"endTs": 1741357232086
2025-03-07 17:53:27 +02:00
},
2025-03-10 16:09:42 +02:00
"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
}]
2025-03-07 17:53:27 +02:00
}
2025-03-10 16:09:42 +02:00
}
```
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
**Usage:**
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
```javascript
var mergedData = temperature.merge(humidity, { ignoreNaN: false });
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
**Output:**
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"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]
}]
2025-03-07 17:53:27 +02:00
}
2025-03-10 16:09:42 +02:00
}
```
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
**Usage:**
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
```javascript
var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true });
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
**Output:**
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"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]
}]
2025-03-07 17:53:27 +02:00
}
2025-03-10 16:09:42 +02:00
}
```
2025-03-07 17:53:27 +02:00
2025-03-10 16:09:42 +02:00
**Example: Freezer temperature analysis**
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
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).
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```javascript
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
}
}
});
}
}
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
return result;
}
```
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
The result is a list of issues that may be used to configure alarm rules:
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```json
[{
"ts": 1741613833843,
"values": {
"issue": {
"temperature": -3.12,
"defrostState": false
}
}
}, {
"ts": 1741613923848,
"values": {
"issue": {
"temperature": -4.16,
"defrostState": false
}
}
}]
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
### Function return format
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
The return format depends on the output type configured in the calculated field settings (default: **Time Series** ).
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
##### Time Series Output
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
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):
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
Without timestamp:
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
}
2025-03-03 11:04:10 +02:00
}
```
2025-03-10 16:09:42 +02:00
With timestamp:
2025-03-03 11:04:10 +02:00
2025-03-10 16:09:42 +02:00
```json
{
"ts": 1740644636669,
"values": {
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
}
}
}
```
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
Array containing multiple timestamps and different values of the `airDensity` :
2025-03-10 10:42:46 +02:00
2025-03-10 16:09:42 +02:00
```json
[
2025-03-03 11:04:10 +02:00
{
"ts": 1740644636669,
"values": {
2025-03-10 16:09:42 +02:00
"airDensity": 1.06
}
},
2025-03-03 11:04:10 +02:00
{
2025-03-10 16:09:42 +02:00
"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):
```json
{
"airDensity": 1.06,
"humidity": 70,
"hvacEnabled": true,
"hvacState": "IDLE",
"configuration": {
"someNumber": 42,
"someArray": [1,2,3],
"someNestedObject": {"key": "value"}
2025-03-03 11:04:10 +02:00
}
2025-03-10 16:09:42 +02:00
}
```