Commit 3c5bce7d20509113efd5ae304eb298ea85016d71

Authored by YevhenBondarenko
Committed by Andrew Shvayka
1 parent aa61db22

Updated thermostat demo

... ... @@ -219,8 +219,8 @@
219 219 "defaultPageSize": 10,
220 220 "defaultSortOrder": "-createdTime",
221 221 "enableSelectColumnDisplay": false,
222   - "enableStatusFilter": true,
223   - "alarmsTitle": "Alarms"
  222 + "alarmsTitle": "Alarms",
  223 + "enableFilter": true
224 224 },
225 225 "title": "New Alarms table",
226 226 "dropShadow": true,
... ... @@ -234,6 +234,9 @@
234 234 "showLegend": false,
235 235 "alarmSource": {
236 236 "type": "entity",
  237 + "name": "alarms",
  238 + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
  239 + "filterId": null,
237 240 "dataKeys": [
238 241 {
239 242 "name": "createdTime",
... ... @@ -275,9 +278,7 @@
275 278 "settings": {},
276 279 "_hash": 0.7977920750136249
277 280 }
278   - ],
279   - "entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
280   - "name": "alarms"
  281 + ]
281 282 },
282 283 "alarmSearchStatus": "ANY",
283 284 "alarmsPollingInterval": 5,
... ... @@ -1031,7 +1032,8 @@
1031 1032 "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
1032 1033 "useLabelFunction": true,
1033 1034 "provider": "openstreet-map",
1034   - "draggableMarker": true
  1035 + "draggableMarker": true,
  1036 + "editablePolygon": true
1035 1037 },
1036 1038 "title": "New Markers Placement - OpenStreetMap",
1037 1039 "dropShadow": true,
... ... @@ -1062,61 +1064,6 @@
1062 1064 "displayTimewindow": true
1063 1065 },
1064 1066 "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
1065   - },
1066   - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
1067   - "isSystemType": true,
1068   - "bundleAlias": "input_widgets",
1069   - "typeAlias": "update_double_timeseries",
1070   - "type": "latest",
1071   - "title": "New widget",
1072   - "sizeX": 7.5,
1073   - "sizeY": 3,
1074   - "config": {
1075   - "datasources": [
1076   - {
1077   - "type": "entity",
1078   - "name": null,
1079   - "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
1080   - "dataKeys": [
1081   - {
1082   - "name": "temperature",
1083   - "type": "timeseries",
1084   - "label": "temperature",
1085   - "color": "#2196f3",
1086   - "settings": {},
1087   - "_hash": 0.4164505192982848
1088   - }
1089   - ]
1090   - }
1091   - ],
1092   - "timewindow": {
1093   - "realtime": {
1094   - "timewindowMs": 60000
1095   - }
1096   - },
1097   - "showTitle": true,
1098   - "backgroundColor": "#fff",
1099   - "color": "rgba(0, 0, 0, 0.87)",
1100   - "padding": "8px",
1101   - "settings": {
1102   - "showResultMessage": true,
1103   - "showLabel": true
1104   - },
1105   - "title": "New Update double timeseries",
1106   - "dropShadow": true,
1107   - "enableFullscreen": false,
1108   - "widgetStyle": {},
1109   - "titleStyle": {
1110   - "fontSize": "16px",
1111   - "fontWeight": 400
1112   - },
1113   - "useDashboardTimewindow": true,
1114   - "showLegend": false,
1115   - "actions": {}
1116   - },
1117   - "row": 0,
1118   - "col": 0,
1119   - "id": "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3"
1120 1067 }
1121 1068 },
1122 1069 "states": {
... ... @@ -1215,12 +1162,6 @@
1215 1162 "sizeY": 6,
1216 1163 "row": 6,
1217 1164 "col": 0
1218   - },
1219   - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
1220   - "sizeX": 7.5,
1221   - "sizeY": 3,
1222   - "row": 12,
1223   - "col": 0
1224 1165 }
1225 1166 },
1226 1167 "gridSettings": {
... ... @@ -1257,16 +1198,6 @@
1257 1198 "stateEntityParamName": null,
1258 1199 "defaultStateEntity": null
1259 1200 }
1260   - },
1261   - "ce27a9d0-93bf-b7a4-054d-d0369a8cf813": {
1262   - "id": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
1263   - "alias": "Thermostat-alarm",
1264   - "filter": {
1265   - "type": "entityName",
1266   - "resolveMultiple": false,
1267   - "entityType": "ASSET",
1268   - "entityNameFilter": "Thermostat Alarms"
1269   - }
1270 1201 }
1271 1202 },
1272 1203 "timewindow": {
... ... @@ -1301,7 +1232,8 @@
1301 1232 "showDashboardTimewindow": true,
1302 1233 "showDashboardExport": true,
1303 1234 "toolbarAlwaysOpen": true
1304   - }
  1235 + },
  1236 + "filters": {}
1305 1237 },
1306 1238 "name": "Thermostats"
1307 1239 }
\ No newline at end of file
... ...
1 1 {
2 2 "ruleChain": {
3   - "additionalInfo": null,
  3 + "additionalInfo": {
  4 + "description": ""
  5 + },
4 6 "name": "Thermostat Alarms",
5 7 "firstRuleNodeId": null,
6 8 "root": false,
... ... @@ -8,131 +10,126 @@
8 10 "configuration": null
9 11 },
10 12 "metadata": {
11   - "firstNodeIndex": 5,
  13 + "firstNodeIndex": 6,
12 14 "nodes": [
13 15 {
14 16 "additionalInfo": {
15   - "layoutX": 929,
16   - "layoutY": 67
  17 + "layoutX": 822,
  18 + "layoutY": 294
17 19 },
18   - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
19   - "name": "Create Temp Alarm",
  20 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  21 + "name": "Save Timeseries",
20 22 "debugMode": false,
21 23 "configuration": {
22   - "alarmType": "High Temperature",
23   - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;",
24   - "severity": "MAJOR",
25   - "propagate": true,
26   - "useMessageAlarmData": false,
27   - "relationTypes": [
28   - "ToAlarmPropagationAsset"
29   - ]
  24 + "defaultTTL": 0
30 25 }
31 26 },
32 27 {
33 28 "additionalInfo": {
34   - "layoutX": 930,
35   - "layoutY": 201
  29 + "description": null,
  30 + "layoutX": 824,
  31 + "layoutY": 221
36 32 },
37   - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
38   - "name": "Clear Temp Alarm",
  33 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  34 + "name": "Save Client Attributes",
39 35 "debugMode": false,
40 36 "configuration": {
41   - "alarmType": "High Temperature",
42   - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
  37 + "scope": "SERVER_SCOPE",
  38 + "notifyDevice": null
43 39 }
44 40 },
45 41 {
46 42 "additionalInfo": {
47   - "layoutX": 930,
48   - "layoutY": 131
  43 + "layoutX": 494,
  44 + "layoutY": 309
49 45 },
50   - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
51   - "name": "Create Humidity Alarm",
  46 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  47 + "name": "Message Type Switch",
52 48 "debugMode": false,
53 49 "configuration": {
54   - "alarmType": "Low Humidity",
55   - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;",
56   - "severity": "MINOR",
57   - "propagate": true,
58   - "useMessageAlarmData": false,
59   - "relationTypes": [
60   - "ToAlarmPropagationAsset"
61   - ]
  50 + "version": 0
62 51 }
63 52 },
64 53 {
65 54 "additionalInfo": {
66   - "layoutX": 929,
67   - "layoutY": 275
  55 + "layoutX": 824,
  56 + "layoutY": 383
68 57 },
69   - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
70   - "name": "Clear Humidity Alarm",
  58 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  59 + "name": "Log RPC from Device",
71 60 "debugMode": false,
72 61 "configuration": {
73   - "alarmType": "Low Humidity",
74   - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
  62 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
75 63 }
76 64 },
77 65 {
78 66 "additionalInfo": {
79   - "layoutX": 586,
80   - "layoutY": 148
  67 + "layoutX": 823,
  68 + "layoutY": 444
81 69 },
82   - "type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode",
83   - "name": "Check Alarms",
  70 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  71 + "name": "Log Other",
84 72 "debugMode": false,
85 73 "configuration": {
86   - "jsScript": "var relations = [];\nif(metadata[\"ss_alarmTemperature\"] === \"true\"){\n if(msg.temperature > metadata[\"ss_thresholdTemperature\"]){\n relations.push(\"NewTempAlarm\");\n } else {\n relations.push(\"ClearTempAlarm\");\n }\n}\nif(metadata[\"ss_alarmHumidity\"] === \"true\"){\n if(msg.humidity < metadata[\"ss_thresholdHumidity\"]){\n relations.push(\"NewHumidityAlarm\");\n } else {\n relations.push(\"ClearHumidityAlarm\");\n }\n}\n\nreturn relations;"
  74 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
87 75 }
88 76 },
89 77 {
90 78 "additionalInfo": {
91   - "layoutX": 321,
92   - "layoutY": 149
  79 + "layoutX": 822,
  80 + "layoutY": 507
93 81 },
94   - "type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode",
95   - "name": "Fetch Configuration",
  82 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  83 + "name": "RPC Call Request",
96 84 "debugMode": false,
97 85 "configuration": {
98   - "clientAttributeNames": [],
99   - "sharedAttributeNames": [],
100   - "serverAttributeNames": [
101   - "alarmTemperature",
102   - "thresholdTemperature",
103   - "alarmHumidity",
104   - "thresholdHumidity"
105   - ],
106   - "latestTsKeyNames": [],
107   - "tellFailureIfAbsent": false,
108   - "getLatestValueWithTs": false
  86 + "timeoutInSeconds": 60
  87 + }
  88 + },
  89 + {
  90 + "additionalInfo": {
  91 + "description": "",
  92 + "layoutX": 209,
  93 + "layoutY": 307
  94 + },
  95 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  96 + "name": "Device Profile Node",
  97 + "debugMode": false,
  98 + "configuration": {
  99 + "persistAlarmRulesState": false,
  100 + "fetchAlarmRulesStateOnStart": false
109 101 }
110 102 }
111 103 ],
112 104 "connections": [
113 105 {
114   - "fromIndex": 4,
115   - "toIndex": 0,
116   - "type": "NewTempAlarm"
  106 + "fromIndex": 2,
  107 + "toIndex": 4,
  108 + "type": "Other"
117 109 },
118 110 {
119   - "fromIndex": 4,
  111 + "fromIndex": 2,
120 112 "toIndex": 1,
121   - "type": "ClearTempAlarm"
  113 + "type": "Post attributes"
122 114 },
123 115 {
124   - "fromIndex": 4,
125   - "toIndex": 2,
126   - "type": "NewHumidityAlarm"
  116 + "fromIndex": 2,
  117 + "toIndex": 0,
  118 + "type": "Post telemetry"
127 119 },
128 120 {
129   - "fromIndex": 4,
  121 + "fromIndex": 2,
130 122 "toIndex": 3,
131   - "type": "ClearHumidityAlarm"
  123 + "type": "RPC Request from Device"
132 124 },
133 125 {
134   - "fromIndex": 5,
135   - "toIndex": 4,
  126 + "fromIndex": 2,
  127 + "toIndex": 5,
  128 + "type": "RPC Request to Device"
  129 + },
  130 + {
  131 + "fromIndex": 6,
  132 + "toIndex": 2,
136 133 "type": "Success"
137 134 }
138 135 ],
... ...
... ... @@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer;
28 28 import org.thingsboard.server.common.data.DataConstants;
29 29 import org.thingsboard.server.common.data.Device;
30 30 import org.thingsboard.server.common.data.DeviceProfile;
  31 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
  32 +import org.thingsboard.server.common.data.DeviceProfileType;
  33 +import org.thingsboard.server.common.data.DeviceTransportType;
31 34 import org.thingsboard.server.common.data.Tenant;
32 35 import org.thingsboard.server.common.data.TenantProfile;
33   -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
34   -import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
35 36 import org.thingsboard.server.common.data.User;
36   -import org.thingsboard.server.common.data.asset.Asset;
  37 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  38 +import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  39 +import org.thingsboard.server.common.data.device.profile.AlarmRule;
  40 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  41 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
  42 +import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
  43 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  44 +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
  45 +import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
37 46 import org.thingsboard.server.common.data.id.CustomerId;
38 47 import org.thingsboard.server.common.data.id.DeviceId;
39 48 import org.thingsboard.server.common.data.id.DeviceProfileId;
... ... @@ -42,19 +51,29 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
42 51 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
43 52 import org.thingsboard.server.common.data.kv.DoubleDataEntry;
44 53 import org.thingsboard.server.common.data.kv.LongDataEntry;
45   -import org.thingsboard.server.common.data.relation.EntityRelation;
  54 +import org.thingsboard.server.common.data.page.PageLink;
  55 +import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
  56 +import org.thingsboard.server.common.data.query.DynamicValue;
  57 +import org.thingsboard.server.common.data.query.DynamicValueSourceType;
  58 +import org.thingsboard.server.common.data.query.EntityKey;
  59 +import org.thingsboard.server.common.data.query.EntityKeyType;
  60 +import org.thingsboard.server.common.data.query.EntityKeyValueType;
  61 +import org.thingsboard.server.common.data.query.FilterPredicateValue;
  62 +import org.thingsboard.server.common.data.query.KeyFilter;
  63 +import org.thingsboard.server.common.data.query.NumericFilterPredicate;
46 64 import org.thingsboard.server.common.data.security.Authority;
47 65 import org.thingsboard.server.common.data.security.DeviceCredentials;
48 66 import org.thingsboard.server.common.data.security.UserCredentials;
  67 +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
  68 +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
49 69 import org.thingsboard.server.common.data.widget.WidgetsBundle;
50   -import org.thingsboard.server.dao.asset.AssetService;
51 70 import org.thingsboard.server.dao.attributes.AttributesService;
52 71 import org.thingsboard.server.dao.customer.CustomerService;
53 72 import org.thingsboard.server.dao.device.DeviceCredentialsService;
54 73 import org.thingsboard.server.dao.device.DeviceProfileService;
55 74 import org.thingsboard.server.dao.device.DeviceService;
56 75 import org.thingsboard.server.dao.exception.DataValidationException;
57   -import org.thingsboard.server.dao.relation.RelationService;
  76 +import org.thingsboard.server.dao.rule.RuleChainService;
58 77 import org.thingsboard.server.dao.settings.AdminSettingsService;
59 78 import org.thingsboard.server.dao.tenant.TenantProfileService;
60 79 import org.thingsboard.server.dao.tenant.TenantService;
... ... @@ -62,6 +81,8 @@ import org.thingsboard.server.dao.user.UserService;
62 81 import org.thingsboard.server.dao.widget.WidgetsBundleService;
63 82
64 83 import java.util.Arrays;
  84 +import java.util.Collections;
  85 +import java.util.LinkedHashMap;
65 86
66 87 @Service
67 88 @Profile("install")
... ... @@ -97,12 +118,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
97 118 private CustomerService customerService;
98 119
99 120 @Autowired
100   - private RelationService relationService;
101   -
102   - @Autowired
103   - private AssetService assetService;
104   -
105   - @Autowired
106 121 private DeviceService deviceService;
107 122
108 123 @Autowired
... ... @@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
114 129 @Autowired
115 130 private DeviceCredentialsService deviceCredentialsService;
116 131
  132 + @Autowired
  133 + private RuleChainService ruleChainService;
  134 +
117 135 @Bean
118 136 protected BCryptPasswordEncoder passwordEncoder() {
119 137 return new BCryptPasswordEncoder();
... ... @@ -245,19 +263,133 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
245 263 createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
246 264 "Raspberry Pi GPIO control sample application");
247 265
248   - Asset thermostatAlarms = new Asset();
249   - thermostatAlarms.setTenantId(demoTenant.getId());
250   - thermostatAlarms.setName("Thermostat Alarms");
251   - thermostatAlarms.setType("AlarmPropagationAsset");
252   - thermostatAlarms = assetService.saveAsset(thermostatAlarms);
253   -
254   - DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat");
255   -
256   - DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
257   - DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
258   -
259   - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset"));
260   - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset"));
  266 + DeviceProfile thermostatDeviceProfile = new DeviceProfile();
  267 + thermostatDeviceProfile.setTenantId(demoTenant.getId());
  268 + thermostatDeviceProfile.setDefault(false);
  269 + thermostatDeviceProfile.setName("thermostat");
  270 + thermostatDeviceProfile.setType(DeviceProfileType.DEFAULT);
  271 + thermostatDeviceProfile.setTransportType(DeviceTransportType.DEFAULT);
  272 + thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
  273 + thermostatDeviceProfile.setDescription("Thermostat device profile");
  274 + thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains(
  275 + demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId());
  276 +
  277 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  278 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  279 + DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration();
  280 + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null);
  281 + deviceProfileData.setConfiguration(configuration);
  282 + deviceProfileData.setTransportConfiguration(transportConfiguration);
  283 + deviceProfileData.setProvisionConfiguration(provisionConfiguration);
  284 + thermostatDeviceProfile.setProfileData(deviceProfileData);
  285 +
  286 + DeviceProfileAlarm highTemperature = new DeviceProfileAlarm();
  287 + highTemperature.setId("highTemperatureAlarmID");
  288 + highTemperature.setAlarmType("High Temperature");
  289 + AlarmRule temperatureRule = new AlarmRule();
  290 + AlarmCondition temperatureCondition = new AlarmCondition();
  291 + temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
  292 +
  293 + KeyFilter alarmTemperatureAttributeFilter = new KeyFilter();
  294 + alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature"));
  295 + alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
  296 + BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate();
  297 + alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
  298 + alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
  299 + alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate);
  300 +
  301 + KeyFilter temperatureTimeseriesFilter = new KeyFilter();
  302 + temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
  303 + temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
  304 + NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
  305 + temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  306 + FilterPredicateValue<Double> temperatureTimeseriesPredicateValue =
  307 + new FilterPredicateValue<>(25.0, null,
  308 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
  309 + temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue);
  310 + temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate);
  311 + temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter));
  312 + temperatureRule.setAlarmDetails("Current temperature = ${temperature}");
  313 + temperatureRule.setCondition(temperatureCondition);
  314 + highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule)));
  315 +
  316 + AlarmRule clearTemperatureRule = new AlarmRule();
  317 + AlarmCondition clearTemperatureCondition = new AlarmCondition();
  318 + clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
  319 +
  320 + KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();
  321 + clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
  322 + clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
  323 + NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
  324 + clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
  325 + FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue =
  326 + new FilterPredicateValue<>(25.0, null,
  327 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
  328 +
  329 + clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue);
  330 + clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate);
  331 + clearTemperatureCondition.setCondition(Collections.singletonList(clearTemperatureTimeseriesFilter));
  332 + clearTemperatureRule.setCondition(clearTemperatureCondition);
  333 + clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}");
  334 + highTemperature.setClearRule(clearTemperatureRule);
  335 +
  336 + DeviceProfileAlarm lowHumidity = new DeviceProfileAlarm();
  337 + lowHumidity.setId("lowHumidityAlarmID");
  338 + lowHumidity.setAlarmType("Low Humidity");
  339 + AlarmRule humidityRule = new AlarmRule();
  340 + AlarmCondition humidityCondition = new AlarmCondition();
  341 + humidityCondition.setSpec(new SimpleAlarmConditionSpec());
  342 +
  343 + KeyFilter alarmHumidityAttributeFilter = new KeyFilter();
  344 + alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity"));
  345 + alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
  346 + BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate();
  347 + alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
  348 + alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
  349 + alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate);
  350 +
  351 + KeyFilter humidityTimeseriesFilter = new KeyFilter();
  352 + humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
  353 + humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
  354 + NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
  355 + humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
  356 + FilterPredicateValue<Double> humidityTimeseriesPredicateValue =
  357 + new FilterPredicateValue<>(60.0, null,
  358 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
  359 + humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue);
  360 + humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate);
  361 + humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter));
  362 +
  363 + humidityRule.setCondition(humidityCondition);
  364 + humidityRule.setAlarmDetails("Current humidity = ${humidity}");
  365 + lowHumidity.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule)));
  366 +
  367 + AlarmRule clearHumidityRule = new AlarmRule();
  368 + AlarmCondition clearHumidityCondition = new AlarmCondition();
  369 + clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
  370 +
  371 + KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();
  372 + clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
  373 + clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
  374 + NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
  375 + clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
  376 + FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue =
  377 + new FilterPredicateValue<>(60.0, null,
  378 + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
  379 +
  380 + clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue);
  381 + clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate);
  382 + clearHumidityCondition.setCondition(Collections.singletonList(clearHumidityTimeseriesFilter));
  383 + clearHumidityRule.setCondition(clearHumidityCondition);
  384 + clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}");
  385 + lowHumidity.setClearRule(clearHumidityRule);
  386 +
  387 + deviceProfileData.setAlarms(Arrays.asList(highTemperature, lowHumidity));
  388 +
  389 + DeviceProfile savedThermostatDeviceProfile = deviceProfileService.saveDeviceProfile(thermostatDeviceProfile);
  390 +
  391 + DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  392 + DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
261 393
262 394 attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE,
263 395 Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
... ...