From bd43b7ae6183907667b6f3e42a97f0d93bbca837 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 3 Mar 2025 11:04:10 +0200 Subject: [PATCH 1/3] added expression function description --- .../en_US/calculated-field/expression_fn.md | 188 +++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index f8173dc528..eb06cda0cb 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1 +1,187 @@ - +#### Calculated field TBEL script function + +The **calculate()** function is user-defined and receives arguments configured by the user to generate new telemetry data. +It allows you to perform custom calculations using [TBEL{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/). + +##### Function signature + +```javascript + function calculate(arg1, arg2, ...): object | object[] +``` + +The function automatically receives user-defined arguments **(arg1, arg2, ...)** and should return a JSON object. + +##### Example: Air Density Calculation + +```javascript +function calculate(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.value), 5.25588); + + // Air density formula + var airDensity = pressure / (287.05 * temperatureK); + + return { + "airDensity": airDensity + }; +} +``` + +##### Function arguments + +The function receives user-configured arguments, which can be of two types: + +* single value arguments - represent the latest telemetry data or attribute. + ```json + { + "altitude": { + "ts": 1740644636669, + "value": 1034 + } + } + ``` + + Use dot notation (`.`) to access argument properties. + + ```javascript + var altitudeTimestamp = altitude.ts; + var altitudeValue = altitude.value; + ``` + +* time series rolling arguments - contain historical data within a defined time window. + ```json + { + "temperature": { + "timeWindow": { + "startTs": 1740643762896, + "endTs": 1740644662896, + "limit": 5000 + }, + "values": [ + { "ts": 1740644355935, "value": 72.32 }, + { "ts": 1740644365935, "value": 72.86 }, + { "ts": 1740644375935, "value": 73.58 }, + { "ts": 1740644385935, "value": "NaN" } + ] + } + } + ``` + + Use dot notation (`.`) to access argument properties. + + ```javascript + var startOfInterval = temperature.timeWindow.startTs; + var firstTimestamp = temperature.values[0].ts; + var firstValue = temperature.values[0].value; + ``` + +**Built-in methods for rolling arguments** + +Time series rolling arguments provide built-in functions for calculations. +These functions accept an optional 'ignoreNaN' boolean parameter, which controls how NaN values are handled. +Each method has two function signatures: + +* **Without parameters:** `method()` → called **without parameters** and defaults to `ignoreNaN = true`, meaning NaN values are ignored. +* **With an explicit parameter:** `method(boolean ignoreNaN)` → called with a boolean `ignoreNaN` parameter: + * `true` → ignores NaN values (default behavior). + * `false` → includes NaN values in calculations. + +| 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 only valid (non-NaN) values. | Counts all values, including NaN. | +| `last()` | Returns the most recent non-NaN value. | Returns the last value, even if it is NaN. | +| `first()` | Returns the oldest non-NaN value. | Returns the first value, even if it is NaN. | +| `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | + +##### The following calculations are executed over the provided above arguments: + +**Example usage: default (`ignoreNaN = true`)** + +```javascript +var avgTemp = temperature.mean(); +var tempMax = temperature.max(); +var valueCount = temperature.count(); +``` + +**Output:** + +```json +{ + "avgTemp": 72.92, + "tempMax": 73.58, + "valueCount": 3 +} +``` + +**Example usage: explicit (`ignoreNaN = false`)** + +```javascript +var avgTemp = temperature.mean(false); // Returns NaN if any NaN values exist +var tempMax = temperature.max(false); // Returns NaN if any NaN values exist +var valueCount = temperature.count(false); // Counts all values, including NaN +``` + +**Output:** + +```json +{ + "avgTemp": "NaN", + "tempMax": "NaN", + "valueCount": 4 +} +``` + +##### Function return format: + +The script should return a JSON object formatted according to the [ThingsBoard Telemetry Upload API](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/telemetry/#time-series-data-upload-api/). +The return value must match one of the supported telemetry upload formats. +**Example Formats**: + +Single key-value format: + +```json +{ + "airDensity": 1.06 +} +``` + +Key-value format with a timestamp: + +```json +{ + "ts": 1740644636669, + "values": { + "airDensity": 1.06 + } +} +``` + +Array of telemetry entries: + +```json +[ + { + "ts": 1740644636669, + "values": { + "airDensity": 1.06 + } + }, + { + "ts": 1740644662896, + "values": [ + { "ts": 1740644355935, "value": 72.32 }, + { "ts": 1740644365935, "value": 72.86 }, + { "ts": 1740644375935, "value": 73.58 }, + { "ts": 1740644385935, "value": "NaN" } + ] + } +] +``` From 83a83c7ae552618caf2acd6dfa0afa3d51d3094c Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 7 Mar 2025 17:53:27 +0200 Subject: [PATCH 2/3] updated page --- .../en_US/calculated-field/expression_fn.md | 198 +++++++++++++++--- 1 file changed, 171 insertions(+), 27 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index eb06cda0cb..2e764d7c16 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1,25 +1,31 @@ #### Calculated field TBEL script function -The **calculate()** function is user-defined and receives arguments configured by the user to generate new telemetry data. -It allows you to perform custom calculations using [TBEL{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/). +The **calculate()** function is user-defined script that takes values of arguments configured in the calculated field setup and an additional `ctx` object containing all arguments. + +The **calculate()** function is a user-defined script that allows you to perform custom calculations using [TBEL{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. +It receives user-configured arguments and an additional `ctx` object, which provides access to all arguments. ##### Function signature ```javascript - function calculate(arg1, arg2, ...): object | object[] + function calculate(ctx, arg1, arg2, ...): object | object[] ``` -The function automatically receives user-defined arguments **(arg1, arg2, ...)** and should return a JSON object. +* the function automatically receives a `ctx` object containing all arguments. +* user-defined arguments `(arg1, arg2, ...)` are passed based on the calculated field configuration. +* the function must return a JSON object (single result) or an array of objects (multiple telemetry entries). ##### Example: Air Density Calculation +This example demonstrates how to calculate air density using altitude and temperature telemetry data. + ```javascript -function calculate(altitude, temperature) { +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.value), 5.25588); + var pressure = 101325 * Math.pow((1 - 2.25577e-5 * altitude), 5.25588); // Air density formula var airDensity = pressure / (287.05 * temperatureK); @@ -32,7 +38,21 @@ function calculate(altitude, temperature) { ##### Function arguments -The function receives user-configured arguments, which can be of two types: +* `ctx` - context object containing all predefined arguments. + + Use `ctx.args.argName` to access arguments. + + ```javascript + var altitude = ctx.args.altitude.ts; + var temperature = ctx.args.temperature; + ``` + +* `arg1, arg2, ...` - user-defined arguments configured in the calculated field setup. These arguments follow the previously described types: + * if an argument represents the latest telemetry data or attribute, it will be passed as a direct value. + + * if an argument is a time series rolling argument, it will be provided as a time series rolling argument described above. + +The **calculate()** function receives predefined arguments, which fall into two categories: * single value arguments - represent the latest telemetry data or attribute. ```json @@ -44,11 +64,11 @@ The function receives user-configured arguments, which can be of two types: } ``` - Use dot notation (`.`) to access argument properties. +Use dot notation (`.`) to access argument properties. ```javascript - var altitudeTimestamp = altitude.ts; - var altitudeValue = altitude.value; + var altitudeTimestamp = ctx.args.altitude.ts; + var altitudeValue = ctx.args.altitude.value; ``` * time series rolling arguments - contain historical data within a defined time window. @@ -57,8 +77,7 @@ The function receives user-configured arguments, which can be of two types: "temperature": { "timeWindow": { "startTs": 1740643762896, - "endTs": 1740644662896, - "limit": 5000 + "endTs": 1740644662896 }, "values": [ { "ts": 1740644355935, "value": 72.32 }, @@ -69,8 +88,8 @@ The function receives user-configured arguments, which can be of two types: } } ``` - - Use dot notation (`.`) to access argument properties. + + Use dot notation (`.`) to access argument properties. ```javascript var startOfInterval = temperature.timeWindow.startTs; @@ -81,27 +100,27 @@ The function receives user-configured arguments, which can be of two types: **Built-in methods for rolling arguments** Time series rolling arguments provide built-in functions for calculations. -These functions accept an optional 'ignoreNaN' boolean parameter, which controls how NaN values are handled. -Each method has two function signatures: +These functions accept an optional `ignoreNaN` boolean parameter, which controls how NaN values are handled. +Each function has two function signatures: * **Without parameters:** `method()` → called **without parameters** and defaults to `ignoreNaN = true`, meaning NaN values are ignored. * **With an explicit parameter:** `method(boolean ignoreNaN)` → called with a boolean `ignoreNaN` parameter: * `true` → ignores NaN values (default behavior). * `false` → includes NaN values in calculations. -| 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. | +| 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 only valid (non-NaN) values. | Counts all values, including NaN. | -| `last()` | Returns the most recent non-NaN value. | Returns the last value, even if it is NaN. | -| `first()` | Returns the oldest non-NaN value. | Returns the first value, even if it is NaN. | -| `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | +| `count()` | Counts only valid (non-NaN) values. | Counts all values, including NaN. | +| `last()` | Returns the most recent non-NaN value. | Returns the last value, even if it is NaN. | +| `first()` | Returns the oldest non-NaN value. | Returns the first value, even if it is NaN. | +| `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | -##### The following calculations are executed over the provided above arguments: +The following calculations are executed over the provided above arguments: **Example usage: default (`ignoreNaN = true`)** @@ -139,6 +158,131 @@ var valueCount = temperature.count(false); // Counts all values, including NaN } ``` +Time series rolling arguments can be merged for multi-sensor analysis. + +Function Signatures: + +* `merge(other, settings)` - merges the current rolling argument with another rolling argument by aligning timestamps and combining values. + Parameters: + * `other` - another time series rolling argument to merge with. + * `settings` (optional) - configuration object, supports: + * `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored. + * `timeWindow` (object, default {}) - defines a custom time window for filtering merged values. + Returns: an object with time window and values from each provided time series rolling arguments. + + * `mergeAll(others, settings)` - merges the current rolling argument with multiple rolling arguments by aligning timestamps and combining values. + Parameters: + * `others` - an array of time series rolling arguments to merge with. + * `settings` (optional) - same as `merge()`. + Returns: an object with timeWindow and aligned values. + +```json +{ + "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 + }] + } +} +``` + +**Example usage** +```javascript +var mergedData = temperature.merge(humidity, { ignoreNaN: false }); +``` + +**Output:** +```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] + }] + } +} +``` + +**Example usage** +```javascript +var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true }); +``` + +**Output:** +```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] + }] + } +} +``` + ##### Function return format: The script should return a JSON object formatted according to the [ThingsBoard Telemetry Upload API](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/telemetry/#time-series-data-upload-api/). From 83f265e6a9af365047f188ad27bb9581e8ddd044 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Mon, 10 Mar 2025 10:42:46 +0200 Subject: [PATCH 3/3] updated help page --- .../en_US/calculated-field/expression_fn.md | 514 +++++++++--------- 1 file changed, 259 insertions(+), 255 deletions(-) diff --git a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md index 2e764d7c16..e35572bf97 100644 --- a/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md +++ b/ui-ngx/src/assets/help/en_US/calculated-field/expression_fn.md @@ -1,9 +1,7 @@ #### Calculated field TBEL script function -The **calculate()** function is user-defined script that takes values of arguments configured in the calculated field setup and an additional `ctx` object containing all arguments. - The **calculate()** function is a user-defined script that allows you to perform custom calculations using [TBEL{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/tbel/) on telemetry and attribute data. -It receives user-configured arguments and an additional `ctx` object, which provides access to all arguments. +It receives arguments configured in the calculated field setup and an additional `ctx` object, which provides access to all arguments. ##### Function signature @@ -11,48 +9,11 @@ It receives user-configured arguments and an additional `ctx` object, which prov function calculate(ctx, arg1, arg2, ...): object | object[] ``` -* the function automatically receives a `ctx` object containing all arguments. -* user-defined arguments `(arg1, arg2, ...)` are passed based on the calculated field configuration. -* the function must return a JSON object (single result) or an array of objects (multiple telemetry entries). +##### Argument representation in the script -##### Example: Air Density Calculation +Before describing how arguments are passed to the function, let's define how different argument types are **represented** inside the script. -This example demonstrates how to calculate air density using altitude and temperature telemetry data. - -```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 - - // 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 - }; -} -``` - -##### Function arguments - -* `ctx` - context object containing all predefined arguments. - - Use `ctx.args.argName` to access arguments. - - ```javascript - var altitude = ctx.args.altitude.ts; - var temperature = ctx.args.temperature; - ``` - -* `arg1, arg2, ...` - user-defined arguments configured in the calculated field setup. These arguments follow the previously described types: - * if an argument represents the latest telemetry data or attribute, it will be passed as a direct value. - - * if an argument is a time series rolling argument, it will be provided as a time series rolling argument described above. - -The **calculate()** function receives predefined arguments, which fall into two categories: +There are two types of arguments that can be used in the function: * single value arguments - represent the latest telemetry data or attribute. ```json @@ -64,13 +25,21 @@ The **calculate()** function receives predefined arguments, which fall into two } ``` -Use dot notation (`.`) to access argument properties. + * when accessed via `ctx.args`, they remain objects: ```javascript var altitudeTimestamp = ctx.args.altitude.ts; var altitudeValue = ctx.args.altitude.value; ``` + * when accessed as a **function parameter**, only the value is passed: + + ```javascript + function calculate(ctx, altitude/*(single value argument)*/, temperature/*(time series rolling argument)*/) { + // altitude = 1035 + } + ``` + * time series rolling arguments - contain historical data within a defined time window. ```json { @@ -89,7 +58,7 @@ Use dot notation (`.`) to access argument properties. } ``` - Use dot notation (`.`) to access argument properties. + * when accessed via `ctx.args`, they remain rolling argument objects: ```javascript var startOfInterval = temperature.timeWindow.startTs; @@ -97,235 +66,270 @@ Use dot notation (`.`) to access argument properties. var firstValue = temperature.values[0].value; ``` -**Built-in methods for rolling arguments** + * when accessed as a **function parameter**, they are passed as rolling arguments, retaining their structure: -Time series rolling arguments provide built-in functions for calculations. -These functions accept an optional `ignoreNaN` boolean parameter, which controls how NaN values are handled. -Each function has two function signatures: - -* **Without parameters:** `method()` → called **without parameters** and defaults to `ignoreNaN = true`, meaning NaN values are ignored. -* **With an explicit parameter:** `method(boolean ignoreNaN)` → called with a boolean `ignoreNaN` parameter: - * `true` → ignores NaN values (default behavior). - * `false` → includes NaN values in calculations. - -| 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 only valid (non-NaN) values. | Counts all values, including NaN. | -| `last()` | Returns the most recent non-NaN value. | Returns the last value, even if it is NaN. | -| `first()` | Returns the oldest non-NaN value. | Returns the first value, even if it is NaN. | -| `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | - -The following calculations are executed over the provided above arguments: - -**Example usage: default (`ignoreNaN = true`)** - -```javascript -var avgTemp = temperature.mean(); -var tempMax = temperature.max(); -var valueCount = temperature.count(); -``` - -**Output:** - -```json -{ - "avgTemp": 72.92, - "tempMax": 73.58, - "valueCount": 3 -} -``` - -**Example usage: explicit (`ignoreNaN = false`)** - -```javascript -var avgTemp = temperature.mean(false); // Returns NaN if any NaN values exist -var tempMax = temperature.max(false); // Returns NaN if any NaN values exist -var valueCount = temperature.count(false); // Counts all values, including NaN -``` - -**Output:** - -```json -{ - "avgTemp": "NaN", - "tempMax": "NaN", - "valueCount": 4 -} -``` - -Time series rolling arguments can be merged for multi-sensor analysis. - -Function Signatures: - -* `merge(other, settings)` - merges the current rolling argument with another rolling argument by aligning timestamps and combining values. - Parameters: - * `other` - another time series rolling argument to merge with. - * `settings` (optional) - configuration object, supports: - * `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored. - * `timeWindow` (object, default {}) - defines a custom time window for filtering merged values. - Returns: an object with time window and values from each provided time series rolling arguments. - - * `mergeAll(others, settings)` - merges the current rolling argument with multiple rolling arguments by aligning timestamps and combining values. - Parameters: - * `others` - an array of time series rolling arguments to merge with. - * `settings` (optional) - same as `merge()`. - Returns: an object with timeWindow and aligned values. - -```json -{ - "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 - }] + ```javascript + function calculate(ctx, altitude/*(single value argument)*/, temperature/*(time series rolling argument)*/) { + var avgTemp = temperature.mean(); // Use rolling argument functions } -} -``` + ``` -**Example usage** -```javascript -var mergedData = temperature.merge(humidity, { ignoreNaN: false }); -``` + **Built-in methods for rolling arguments** -**Output:** -```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] - }] + Time series rolling arguments provide built-in functions for calculations. + These functions accept an optional `ignoreNaN` boolean parameter, which controls how NaN values are handled. + Each function has two function signatures: + + * **Without parameters:** `method()` → called **without parameters** and defaults to `ignoreNaN = true`, meaning NaN values are ignored. + * **With an explicit parameter:** `method(boolean ignoreNaN)` → called with a boolean `ignoreNaN` parameter: + * `true` → ignores NaN values (default behavior). + * `false` → includes NaN values in calculations. + + | 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 only valid (non-NaN) values. | Counts all values, including NaN. | + | `last()` | Returns the most recent non-NaN value. | Returns the last value, even if it is NaN. | + | `first()` | Returns the oldest non-NaN value. | Returns the first value, even if it is NaN. | + | `sum()` | Computes the total sum, ignoring NaN values. | Returns NaN if any NaN values exist. | + + The following calculations are executed over the provided above arguments: + + **Usage: default (`ignoreNaN = true`)** + + ```javascript + var avgTemp = temperature.mean(); + var tempMax = temperature.max(); + var valueCount = temperature.count(); + ``` + + **Output:** + + ```json + { + "avgTemp": 72.92, + "tempMax": 73.58, + "valueCount": 3 } -} -``` + ``` -**Example usage** -```javascript -var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true }); -``` + **Usage: explicit (`ignoreNaN = false`)** + + ```javascript + var avgTemp = temperature.mean(false); // Returns NaN if any NaN values exist + var tempMax = temperature.max(false); // Returns NaN if any NaN values exist + var valueCount = temperature.count(false); // Counts all values, including NaN + ``` -**Output:** -```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] - }] + **Output:** + + ```json + { + "avgTemp": "NaN", + "tempMax": "NaN", + "valueCount": 4 } + ``` + + Time series rolling arguments can be **merged** to align timestamps across multiple datasets. + + | Method | Description | Parameters | Returns | + |:-----------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------| + | `merge(other, settings)` | Merges the current rolling argument with another rolling argument by aligning timestamps and filling missing values with the previous available value. |
  • `other` (another rolling argument)
  • `settings` (optional) - configuration object, supports:

    • `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.
    • `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.
| Merged object with timeWindow and aligned values. | + | `mergeAll(others, settings)` | Merges the current rolling argument with multiple rolling arguments by aligning timestamps and filling missing values with the previous available value. |
  • `others` (array of rolling arguments)
  • `settings` (optional) - configuration object, supports:

    • `ignoreNaN` (boolean, default true) - controls whether NaN values should be ignored.
    • `timeWindow` (object, default {}) - defines a custom time window for filtering merged values.
| Merged object with timeWindow and aligned values.| + + **Example arguments:** + + ```json + { + "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:** + + ```javascript + var mergedData = temperature.merge(humidity, { ignoreNaN: false }); + ``` + + **Output:** + + ```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] + }] + } + } + ``` + + **Usage:** + + ```javascript + var mergedData = temperature.mergeAll([humidity, pressure], { ignoreNaN: true }); + ``` + + **Output:** + + ```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] + }] + } + } + ``` + +##### Function arguments + +* `ctx` - context object that contains all provided arguments, in the representations described above. + +Accessing arguments via `ctx`: + + ```javascript + var altitude = ctx.args.altitude; // single value argument + var temperature = ctx.args.temperature; // time series rolling argument + ``` + +* `arg1, arg2, ...` - user-defined arguments configured in the calculated field setup. + +How they are passed depends on their type: + +* **single value arguments** are passed as raw values **(e.g., 22.5, "ON")**. +* **time series rolling arguments** are passed as objects (containing multiple values). + +##### Example: Air Density Calculation + +This function calculates air density using `altitude` (single value argument) and `temperature` (time series rolling argument). + +```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 + + // 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 + }; } ``` -##### Function return format: +* `altitude` is a single value, passed as number. +* `temperature` is a rolling argument, retaining its full structure. + +##### Function return format The script should return a JSON object formatted according to the [ThingsBoard Telemetry Upload API](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/telemetry/#time-series-data-upload-api/). The return value must match one of the supported telemetry upload formats. -**Example Formats**: -Single key-value format: +The script must return data in a format compatible with ThingsBoard’s APIs. +The correct return format depends on the calculated field output configuration: -```json -{ - "airDensity": 1.06 -} -``` +* if latest telemetry is used for output, the function must return data according to the [Telemetry Upload API](${siteBaseUrl}/docs${docPlatformPrefix}/reference/mqtt-api/#attributes-api/). -Key-value format with a timestamp: - -```json -{ - "ts": 1740644636669, - "values": { - "airDensity": 1.06 + * without timestamps + ```json + { + "airDensity": 1.06, + "someKey": "value" } -} -``` - -Array of telemetry entries: - -```json -[ + ``` + * with a timestamp: + ```json { "ts": 1740644636669, "values": { - "airDensity": 1.06 - } - }, - { - "ts": 1740644662896, - "values": [ - { "ts": 1740644355935, "value": 72.32 }, - { "ts": 1740644365935, "value": 72.86 }, - { "ts": 1740644375935, "value": 73.58 }, - { "ts": 1740644385935, "value": "NaN" } - ] + "airDensity": 1.06, + "someKey": "value" + } } -] -``` + ``` + +* if attributes are used for output, the function must return data according to the [Attributes API](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/telemetry/#time-series-data-upload-api/). + + ```json + { + "airDensity": 1.06, + "someKey": "value" + } + ```