Merge pull request #8695 from ShvaykaD/feature/rule-nodes-versioning

added field templatization docs for enrichment rule nodes & added java doc for upgrade method in TbVersionedNode interface
This commit is contained in:
Andrew Shvayka 2023-06-02 16:17:59 +03:00 committed by GitHub
commit 74a44685fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1023 additions and 21 deletions

View File

@ -20,6 +20,16 @@ import org.thingsboard.server.common.data.util.TbPair;
public interface TbVersionedNode extends TbNode { public interface TbVersionedNode extends TbNode {
/**
* Upgrades the configuration from a specific version to the current version specified in the
* {@link RuleNode} annotation for the instance of {@link TbVersionedNode}.
*
* @param fromVersion The version from which the configuration needs to be upgraded.
* @param oldConfiguration The old configuration to be upgraded.
* @return A pair consisting of a Boolean flag indicating the success of the upgrade
* and a JsonNode representing the upgraded configuration.
* @throws TbNodeException If an error occurs during the upgrade process.
*/
TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException; TbPair<Boolean, JsonNode> upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException;
} }

View File

@ -23,7 +23,6 @@ import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.TbRelationTypes;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.TbMsgCallback; import org.thingsboard.server.common.msg.queue.TbMsgCallback;
@ -60,25 +59,23 @@ public abstract class TbAbstractTransformNode<C> implements TbNode {
} }
protected void transformSuccess(TbContext ctx, TbMsg msg, List<TbMsg> msgs) { protected void transformSuccess(TbContext ctx, TbMsg msg, List<TbMsg> msgs) {
if (msgs != null && !msgs.isEmpty()) { if (msgs == null || msgs.isEmpty()) {
if (msgs.size() == 1) {
ctx.tellSuccess(msgs.get(0));
} else {
TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(msgs.size(), new TbMsgCallback() {
@Override
public void onSuccess() {
ctx.ack(msg);
}
@Override
public void onFailure(RuleEngineException e) {
ctx.tellFailure(msg, e);
}
});
msgs.forEach(newMsg -> ctx.enqueueForTellNext(newMsg, TbRelationTypes.SUCCESS, wrapper::onSuccess, wrapper::onFailure));
}
} else {
ctx.tellFailure(msg, new RuntimeException("Message or messages list are empty!")); ctx.tellFailure(msg, new RuntimeException("Message or messages list are empty!"));
} else if (msgs.size() == 1) {
ctx.tellSuccess(msgs.get(0));
} else {
TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(msgs.size(), new TbMsgCallback() {
@Override
public void onSuccess() {
ctx.ack(msg);
}
@Override
public void onFailure(RuleEngineException e) {
ctx.tellFailure(msg, e);
}
});
msgs.forEach(newMsg -> ctx.enqueueForTellNext(newMsg, TbRelationTypes.SUCCESS, wrapper::onSuccess, wrapper::onFailure));
} }
} }

View File

@ -0,0 +1,9 @@
Fields templatization feature allows you to process the incoming messages with dynamic configuration by substitution templates specified in the configuration fields with values from message or message metadata.
There are two types of rule node configuration templates defined:
- `$[messageKey]` - templates with square brackets used to extract value from the message.
- `${metadataKey}` - templates with curly brackets used to extract value from the message metadata.
**Note:** `messageKey` and `metadataKey` are just samples of key names that might exist in the message or metadata.

View File

@ -0,0 +1,107 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's assume that we have a customer-based solution where customer manage two type of devices: `temperature` and `humidity` sensors.
Additionally, let's assume that customer configured the thresholds settings for each device type.
Threshold settings stored as an attributes on a customer level:
- *temperature_min_threshold* and *temperature_max_threshold* for temperature sensor with values set to *10* and *30* accordingly.
- *humidity_min_threshold* and *humidity_max_threshold* for humidity sensor with values set to *70* and *85* accordingly.
Each message received from device includes `deviceType` property in the message metadata
with either `temperature` or `humidity` value according to the sensor type.
In order to fetch the threshold value for the further message processing you can define next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/customer-attributes-ft.png)
Imagine that you receive message defined below from the `temperature` sensor
and forwarded it to the **customer attributes** node with configuration added above.
- incoming message definition:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000"
}
}
```
<br>
The same example for the `humidity` sensor:
- incoming message definition:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000"
}
}
```
<br>
Rule node configuration set to fetch data to the message metadata. In the following way:
- outgoing message for the `temperature` sensor would be updated to:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000",
"min_threshold": "10",
"max_threshold": "30"
}
}
```
<br>
- outgoing message for the `humidity` sensor would be updated to:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000",
"min_threshold": "70",
"max_threshold": "85"
}
}
```
<br>
These examples showcases using the **customer attributes** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,132 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's assume that we have a moisture meter device(message originator) that publishes a telemetry messages that includes the following data readings:
- `soilMoisture`
- `windSpeed`
- `windDirection`
- `temperature`
- `humidity`
Depending on certain conditions, we might need to fetch additional server-side attributes from the moisture meter device.
Specifically, if the soil moisture reading drops below a certain threshold, let's say 30%, this is considered critical as it directly impacts crop health and growth.
In this case, we need to fetch the `lastIrrigationTime` attribute.
This additional information can help us understand when the field was last watered and take necessary action, such as activating the irrigation system.
However, if the soil moisture is above this critical threshold, then we need to check another condition.
If the wind speed is above a certain level, say 8 m/s, we need to fetch the `lastWindSpeedAlarmTime` attribute.
This additional information can help us to understand when the last significant wind event occurred,
which might be indicative of an approaching storm or damaging winds.
Consider that you write a script that depending on a conditions written above will add to the message metadata additional key: `keyToFetch` with one of the next values:
- `lastIrrigationTime`
- `lastWindSpeedAlarmTime`
In order to fetch value of one of the following keys for the further message processing you can define next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/originator-attributes-ft.png)
- message definition that match first condition after processing in the script node:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 28.9,
"windSpeed": 8.2,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "SN-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationTime"
}
}
```
<br>
- message definition that match second condition after processing in the script node:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 32.5,
"windSpeed": 10.4,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "SN-001",
"ts": "1685379440000",
"keyToFetch": "lastWindSpeedAlarmTime"
}
}
```
<br>
Rule node configuration set to fetch data to the message metadata. In the following way:
- outgoing message for the first condition would be updated to:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 28.9,
"windSpeed": 8.2,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "SN-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationTime",
"ss_lastIrrigationTime": "1685369440000"
}
}
```
<br>
- outgoing message for the second condition would be updated to:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 32.5,
"windSpeed": 10.4,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "MM-001",
"ts": "1685379440000",
"keyToFetch": "lastWindSpeedAlarmTime",
"ss_lastWindSpeedAlarmTime": "1685359440000"
}
}
```
<br>
These examples showcases using the **originator attributes** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,106 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's assume that we have two device types in our use case:
- `smart_door_lock`
- `motion_detector`
Let's assume that device of type `dock_lock_sensor` and name `SDL-001` publish next type of messages to the system:
```json
{
"msg": {
"status": "locked"
},
"metadata": {
"deviceName": "SDL-001",
"deviceType": "smart_door_lock",
"ts": "1685379440000"
}
}
```
<br>
and device of type `motion_detector` and name `MD-001` publish next type of messages to the system:
```json
{
"msg": {
"motionDetected": "true"
},
"metadata": {
"deviceName": "MD-001",
"deviceType": "motion_detector",
"ts": "1685379440000"
}
}
```
<br>
Imagine that you send the messages received from the devices to the external systems
and depending on the device type you need add to the message the human-readable label
of the device with field name equal to the `deviceType` value from the message metadata.
Let's assume that devices have next labels:
- *Grocery warehouse door* for `SDL-001` device.
- *Grocery Warehouse motion detector* for `MD-001` device.
In order to fetch labels and add them to the message with the appropriate field name
you can define the next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/originator-fields-ft.png)
<br>
Rule node configuration set to fetch data to the message. In the following way:
- outgoing message for the `SDL-001` device would be updated to:
```json
{
"msg": {
"status": "locked",
"smart_door_lock": "Grocery warehouse door"
},
"metadata": {
"deviceName": "SDL-001",
"deviceType": "smart_door_lock",
"ts": "1685379440000"
}
}
```
<br>
- outgoing message for the `MD-001` device would be updated to:
```json
{
"msg": {
"motionDetected": "true",
"motion_detector": "Grocery Warehouse motion detector"
},
"metadata": {
"deviceName": "MD-001",
"deviceType": "motion_detector",
"ts": "1685379440000"
}
}
```
<br>
These examples showcases using the **originator fields** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,292 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Originator telemetry node support templatization for multiple configuration fields. Namely, you can specify the template in the
*Timeseries keys* list, and also there is an option to use the templatization for the fetch interval start and end if you are using *dynamic interval*.
###### Example 1
Let's start with an example of using templatization for the *Timeseries keys* list.
Imagine that you have a GPS tracker device(message originator) that publishes a telemetry messages that includes the following data readings:
- `latitude` - current device latitude value.
- `longitude` - current device longitude value.
- `event` - parameter that specifies the current state of the device. The value might be *parked* or *motion*.
Additionally let's imagine that devices periodically publishes other telemetry messages that includes additional information such as:
- `speed` - current speed value.
- `direction` - compass direction in which the device is moving.
- `acceleration` - how quickly the speed of the device is changing.
- `fuel_level` - current fuel level.
- `battery_level` - current battery level.
- `parked_location` - precise location where the device is parked.
- `parked_duration` - current park duration value.
- `parked_time` - timestamp when the device was parked.
Let's imagine that we need to make some historical analysis by fetching 3 latest telemetry readings for the keys listed below if the `event` value is set to *motion*:
- `speed`
- `direction`
- `acceleration`
- `fuel_level`
- `battery_level`
Otherwise, if the `event` value is set to *parked* value we need to fetch 3 latest telemetry readings for the following data keys:
- `parked_location`
- `parked_duration`
- `parked_time`
- `fuel_level`
- `battery_level`
Imagine that you created a script node that depending on the `event` value adds to the message metadata appropriate keyToFetch fields.
- message definition that match condition when `event` is set to *motion* value after processing in the script node:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "motion"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685479440000",
"keyToFetch1": "speed",
"keyToFetch2": "direction",
"keyToFetch3": "acceleration"
}
}
```
<br>
- message definition that match condition when `event` is set to *parked* value after processing in the script node:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "parked"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685379440000",
"keyToFetch1": "parked_location",
"keyToFetch2": "parked_duration",
"keyToFetch3": "parked_time"
}
}
```
<br>
In order to fetch the additional telemetry key values to make some historical analysis of the tracker's state you can define the next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/originator-telemetry-ft.png)
![image](${helpBaseUrl}/help/images/rulenode/examples/originator-telemetry-ft-2.png)
<br>
Rule node configuration is set to retrieve the telemetry from the fetch interval with configurable query parameters that you can check above.
So let's imagine that 3 latest values for the keys that we are going to fetch are:
- `speed` - 5.2, 15.7, 30.2 (mph).
- `direction` - N(North), NE(North-East), E(East).
- `acceleration` - 2.2, 2.4, 2.5 (m/s²).
- `fuel_level` - 61.5, 57.4, 55.6 (%).
- `battery_level` - 88.1, 87.8, 87.2 (%).
- `parked_location` - dr5rtwceb (geohash). Same value for 3 latest data readings.
- `parked_duration` - 6300000, 7300000, 8300000 (ms).
- `parked_time` - 1685339240000 (ms). Same value for 3 latest data readings.
In the following way:
- outgoing message for the case when the `event` has value *motion* would be look like this:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "motion"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685479440000",
"keyToFetch1": "speed",
"keyToFetch2": "direction",
"keyToFetch3": "acceleration",
"speed": "[{\"ts\":1685476840000,\"value\":5.2},{\"ts\":1685477840000,\"value\":15.7},{\"ts\":1685478840000,\"value\":30.2}]",
"direction": "[{\"ts\":1685476840000,\"value\":\"N\"},{\"ts\":1685477840000,\"value\":\"NE\"},{\"ts\":1685478840000,\"value\":\"N\"}]",
"acceleration": "[{\"ts\":1685476840000,\"value\":2.2},{\"ts\":1685477840000,\"value\":2.4},{\"ts\":1685478840000,\"value\":2.5}]",
"fuel_level": "[{\"ts\":1685476840000,\"value\":61.5},{\"ts\":1685477840000,\"value\":57.4},{\"ts\":1685478840000,\"value\":55.6}]",
"battery_level": "[{\"ts\":1685476840000,\"value\":88.1},{\"ts\":1685477840000,\"value\":87.8},{\"ts\":1685478840000,\"value\":87.2}]"
}
}
```
<br>
- outgoing message for the case when the `event` has value *parked* would be look like this:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "parked"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685379440000",
"keyToFetch1": "parked_location",
"keyToFetch2": "parked_duration",
"keyToFetch3": "parked_time",
"parked_location": "[{\"ts\":1685376840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685377840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685378840000,\"value\":\"dr5rtwceb\"}]",
"parked_duration": "[{\"ts\":1685376840000,\"value\":6300000},{\"ts\":1685377840000,\"value\":7300000},{\"ts\":1685378840000,\"value\":8300000}]",
"parked_time": "[{\"ts\":1685376840000,\"value\":1685376840000},{\"ts\":1685377840000,\"value\":1685377840000},{\"ts\":1685378840000,\"value\":1685378840000}]",
"fuel_level": "[{\"ts\":1685376840000,\"value\":61.5},{\"ts\":1685377840000,\"value\":57.4},{\"ts\":1685378840000,\"value\":55.6}]",
"battery_level": "[{\"ts\":1685376840000,\"value\":88.1},{\"ts\":1685377840000,\"value\":87.8},{\"ts\":1685378840000,\"value\":87.2}]"
}
}
```
###### Example 2
This example will extend the previous example with additional condition:
Imagine that you need to specify the fetch interval dynamically from the 1 hour ago till the current time.
Additionally let's assume that the current time can be extracted from `ts` field that we have in the message metadata on each message received.
While the value of (1 hour ago) can be calculated in the script node that we use for adding keyToFetch fields into metadata.
In the following way:
- message definition that match condition when `event` is set to *motion* value after processing in the script node:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "motion"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685479440000",
"keyToFetch1": "speed",
"keyToFetch2": "direction",
"keyToFetch3": "acceleration",
"dynamicIntervalStart": "1685475840000"
}
}
```
- message definition that match condition when `event` is set to *parked* value after processing in the script node:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "parked"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685379440000",
"keyToFetch1": "parked_location",
"keyToFetch2": "parked_duration",
"keyToFetch3": "parked_time",
"dynamicIntervalStart": "1685375840000"
}
}
```
<br>
In order to fetch the data using dynamic interval we need enable *Use dynamic interval* option in the rule node configuration and specify the templates for the *Interval start* and *Interval end*:
![image](${helpBaseUrl}/help/images/rulenode/examples/originator-telemetry-ft-3.png)
<br>
Other configuration wasn't change from our previous example.
In the following way:
- outgoing message for the case when the `event` has value *motion* would be look like this:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "motion"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685479440000",
"keyToFetch1": "speed",
"keyToFetch2": "direction",
"keyToFetch3": "acceleration",
"dynamicIntervalStart": "1685475840000",
"speed": "[{\"ts\":1685476840000,\"value\":5.2},{\"ts\":1685477840000,\"value\":15.7},{\"ts\":1685478840000,\"value\":30.2}]",
"direction": "[{\"ts\":1685476840000,\"value\":\"N\"},{\"ts\":1685477840000,\"value\":\"NE\"},{\"ts\":1685478840000,\"value\":\"N\"}]",
"acceleration": "[{\"ts\":1685476840000,\"value\":2.2},{\"ts\":1685477840000,\"value\":2.4},{\"ts\":1685478840000,\"value\":2.5}]",
"fuel_level": "[{\"ts\":1685476840000,\"value\":61.5},{\"ts\":1685477840000,\"value\":57.4},{\"ts\":1685478840000,\"value\":55.6}]",
"battery_level": "[{\"ts\":1685476840000,\"value\":88.1},{\"ts\":1685477840000,\"value\":87.8},{\"ts\":1685478840000,\"value\":87.2}]"
}
}
```
<br>
- outgoing message for the case when the `event` has value *parked* would be look like this:
```json
{
"msg": {
"latitude": "40.730610",
"longitude": "-73.935242",
"event": "parked"
},
"metadata": {
"deviceName": "GPS-001",
"deviceType": "GPS Tracker",
"ts": "1685379440000",
"keyToFetch1": "parked_location",
"keyToFetch2": "parked_duration",
"keyToFetch3": "parked_time",
"dynamicIntervalStart": "1685375840000",
"parked_location": "[{\"ts\":1685376840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685377840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685378840000,\"value\":\"dr5rtwceb\"}]",
"parked_duration": "[{\"ts\":1685376840000,\"value\":6300000},{\"ts\":1685377840000,\"value\":7300000},{\"ts\":1685378840000,\"value\":8300000}]",
"parked_time": "[{\"ts\":1685376840000,\"value\":1685376840000},{\"ts\":1685377840000,\"value\":1685377840000},{\"ts\":1685378840000,\"value\":1685378840000}]",
"fuel_level": "[{\"ts\":1685376840000,\"value\":61.5},{\"ts\":1685377840000,\"value\":57.4},{\"ts\":1685378840000,\"value\":55.6}]",
"battery_level": "[{\"ts\":1685376840000,\"value\":88.1},{\"ts\":1685377840000,\"value\":87.8},{\"ts\":1685378840000,\"value\":87.2}]"
}
}
```
<br>
These examples showcases using the **originator telemetry** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,132 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's assume that we have a moisture meter device(message originator) that publishes a telemetry messages that includes the following data readings:
- `soilMoisture`
- `windSpeed`
- `windDirection`
- `temperature`
- `humidity`
Depending on certain conditions, we might need to fetch additional server-side attributes from the related irrigation controller device.
Specifically, if the soil moisture reading drops below a certain threshold, let's say 30%, this is considered critical as it directly impacts crop health and growth.
In this case, we need to fetch the `lastIrrigationTime` attribute.
This additional information can help us understand when the field was last watered and take necessary action, such as activating the irrigation system.
However, if the soil moisture is above this critical threshold, then we need to check another condition.
If the wind speed is above a certain level, say 8 m/s, we might need to fetch the `lastIrrigationPauseTime` attribute.
This additional information can help us understand when the last time the irrigation system was paused due to high wind conditions,
which might help to make more informed decisions about when and how to irrigate, considering both current and historical weather conditions.
Consider that you write a script that depending on a conditions written above will add to the message metadata additional key: `keyToFetch` with one of the next values:
- `lastIrrigationTime`
- `lastIrrigationPauseTime`
In order to fetch value of one of the following keys for the further message processing you can define next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/related-device-attributes-ft.png)
- message definition that match first condition after processing in the script node:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 28.9,
"windSpeed": 8.2,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "MM-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationTime"
}
}
```
<br>
- message definition that match second condition after processing in the script node:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 32.5,
"windSpeed": 10.4,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "MM-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationPauseTime"
}
}
```
<br>
Rule node configuration set to fetch data to the message metadata. In the following way:
- outgoing message for the first condition would be updated to:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 28.9,
"windSpeed": 8.2,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "MM-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationTime",
"ss_lastIrrigationTime": "1685369440000"
}
}
```
<br>
- outgoing message the second condition would be updated to:
```json
{
"msg": {
"temperature": 26.5,
"humidity": 75.2,
"soilMoisture": 32.5,
"windSpeed": 10.4,
"windDirection": "NNE"
},
"metadata": {
"deviceType": "default",
"deviceName": "MM-001",
"ts": "1685379440000",
"keyToFetch": "lastIrrigationPauseTime",
"ss_lastIrrigationPauseTime": "1685359440000"
}
}
```
<br>
These examples showcases using the **related device attributes** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,111 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's consider a scenario where we possess an asset that serves as a warehouse
and is responsible for overseeing two categories of devices:
- sensors measuring `temperature`.
- sensors measuring `humidity`.
Additionally, let's assume that this asset has configured thresholds set as attributes for each device type:
- *temperature_min_threshold* and *temperature_max_threshold* for temperature sensor with values set to *10* and *30* accordingly.
- *humidity_min_threshold* and *humidity_max_threshold* for humidity sensor with values set to *70* and *85* accordingly.
Each message received from device includes `deviceType` property in the message metadata
with either `temperature` or `humidity` value according to the sensor type.
In order to fetch the threshold value for the further message processing you can define next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/related-entity-data-ft.png)
![image](${helpBaseUrl}/help/images/rulenode/examples/related-entity-data-ft-2.png)
Imagine that you receive message defined below from the `temperature` sensor
and forwarded it to the **related entity data** node with configuration added above.
- incoming message definition:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000"
}
}
```
<br>
The same example for the `humidity` sensor:
- incoming message definition:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000"
}
}
```
<br>
Rule node configuration set to fetch data to the message metadata. In the following way:
- outgoing message for the `temperature` sensor would be updated to:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000",
"min_threshold": "10",
"max_threshold": "30"
}
}
```
<br>
- outgoing message for the `humidity` sensor would be updated to:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000",
"min_threshold": "70",
"max_threshold": "85"
}
}
```
<br>
These examples showcases using the **related entity data** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

View File

@ -0,0 +1,107 @@
#### Fields templatization
<div class="divider"></div>
<br/>
{% include rulenode/common_node_fields_templatization %}
##### Examples
Let's assume that tenant manage two type of devices: `temperature` and `humidity` sensors.
Additionally, let's assume that tenant configured the thresholds settings for each device type.
Threshold settings stored as an attributes on a tenant level:
- *temperature_min_threshold* and *temperature_max_threshold* for temperature sensor with values set to *10* and *30* accordingly.
- *humidity_min_threshold* and *humidity_max_threshold* for humidity sensor with values set to *70* and *85* accordingly.
Each message received from device includes `deviceType` property in the message metadata
with either `temperature` or `humidity` value according to the sensor type.
In order to fetch the threshold value for the further message processing you can define next node configuration:
![image](${helpBaseUrl}/help/images/rulenode/examples/tenant-attributes-ft.png)
Imagine that you receive message defined below from the `temperature` sensor
and forwarded it to the **tenant attributes** node with configuration added above.
- incoming message definition:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000"
}
}
```
<br>
The same example for the `humidity` sensor:
- incoming message definition:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000"
}
}
```
<br>
Rule node configuration set to fetch data to the message metadata. In the following way:
- outgoing message for the `temperature` sensor would be updated to:
```json
{
"msg": {
"temperature": 32
},
"metadata": {
"deviceType": "temperature",
"deviceName": "TH-001",
"ts": "1685379440000",
"min_threshold": "10",
"max_threshold": "30"
}
}
```
<br>
- outgoing message for the `humidity` sensor would be updated to:
```json
{
"msg": {
"humidity": 77
},
"metadata": {
"deviceType": "humidity",
"deviceName": "HM-001",
"ts": "1685379440000",
"min_threshold": "70",
"max_threshold": "85"
}
}
```
<br>
These examples showcases using the **tenant attributes** node with dynamic configuration based on the substitution of metadata fields.
<br>
<br>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB