merge with master
This commit is contained in:
commit
dc310f4cab
@ -219,8 +219,8 @@
|
|||||||
"defaultPageSize": 10,
|
"defaultPageSize": 10,
|
||||||
"defaultSortOrder": "-createdTime",
|
"defaultSortOrder": "-createdTime",
|
||||||
"enableSelectColumnDisplay": false,
|
"enableSelectColumnDisplay": false,
|
||||||
"enableStatusFilter": true,
|
"alarmsTitle": "Alarms",
|
||||||
"alarmsTitle": "Alarms"
|
"enableFilter": true
|
||||||
},
|
},
|
||||||
"title": "New Alarms table",
|
"title": "New Alarms table",
|
||||||
"dropShadow": true,
|
"dropShadow": true,
|
||||||
@ -234,6 +234,9 @@
|
|||||||
"showLegend": false,
|
"showLegend": false,
|
||||||
"alarmSource": {
|
"alarmSource": {
|
||||||
"type": "entity",
|
"type": "entity",
|
||||||
|
"name": "alarms",
|
||||||
|
"entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
|
||||||
|
"filterId": null,
|
||||||
"dataKeys": [
|
"dataKeys": [
|
||||||
{
|
{
|
||||||
"name": "createdTime",
|
"name": "createdTime",
|
||||||
@ -275,9 +278,7 @@
|
|||||||
"settings": {},
|
"settings": {},
|
||||||
"_hash": 0.7977920750136249
|
"_hash": 0.7977920750136249
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
|
|
||||||
"name": "alarms"
|
|
||||||
},
|
},
|
||||||
"alarmSearchStatus": "ANY",
|
"alarmSearchStatus": "ANY",
|
||||||
"alarmsPollingInterval": 5,
|
"alarmsPollingInterval": 5,
|
||||||
@ -1031,7 +1032,8 @@
|
|||||||
"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;",
|
"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;",
|
||||||
"useLabelFunction": true,
|
"useLabelFunction": true,
|
||||||
"provider": "openstreet-map",
|
"provider": "openstreet-map",
|
||||||
"draggableMarker": true
|
"draggableMarker": true,
|
||||||
|
"editablePolygon": true
|
||||||
},
|
},
|
||||||
"title": "New Markers Placement - OpenStreetMap",
|
"title": "New Markers Placement - OpenStreetMap",
|
||||||
"dropShadow": true,
|
"dropShadow": true,
|
||||||
@ -1062,61 +1064,6 @@
|
|||||||
"displayTimewindow": true
|
"displayTimewindow": true
|
||||||
},
|
},
|
||||||
"id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
|
"id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf"
|
||||||
},
|
|
||||||
"f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
|
|
||||||
"isSystemType": true,
|
|
||||||
"bundleAlias": "input_widgets",
|
|
||||||
"typeAlias": "update_double_timeseries",
|
|
||||||
"type": "latest",
|
|
||||||
"title": "New widget",
|
|
||||||
"sizeX": 7.5,
|
|
||||||
"sizeY": 3,
|
|
||||||
"config": {
|
|
||||||
"datasources": [
|
|
||||||
{
|
|
||||||
"type": "entity",
|
|
||||||
"name": null,
|
|
||||||
"entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
|
|
||||||
"dataKeys": [
|
|
||||||
{
|
|
||||||
"name": "temperature",
|
|
||||||
"type": "timeseries",
|
|
||||||
"label": "temperature",
|
|
||||||
"color": "#2196f3",
|
|
||||||
"settings": {},
|
|
||||||
"_hash": 0.4164505192982848
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"timewindow": {
|
|
||||||
"realtime": {
|
|
||||||
"timewindowMs": 60000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"showTitle": true,
|
|
||||||
"backgroundColor": "#fff",
|
|
||||||
"color": "rgba(0, 0, 0, 0.87)",
|
|
||||||
"padding": "8px",
|
|
||||||
"settings": {
|
|
||||||
"showResultMessage": true,
|
|
||||||
"showLabel": true
|
|
||||||
},
|
|
||||||
"title": "New Update double timeseries",
|
|
||||||
"dropShadow": true,
|
|
||||||
"enableFullscreen": false,
|
|
||||||
"widgetStyle": {},
|
|
||||||
"titleStyle": {
|
|
||||||
"fontSize": "16px",
|
|
||||||
"fontWeight": 400
|
|
||||||
},
|
|
||||||
"useDashboardTimewindow": true,
|
|
||||||
"showLegend": false,
|
|
||||||
"actions": {}
|
|
||||||
},
|
|
||||||
"row": 0,
|
|
||||||
"col": 0,
|
|
||||||
"id": "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"states": {
|
"states": {
|
||||||
@ -1215,12 +1162,6 @@
|
|||||||
"sizeY": 6,
|
"sizeY": 6,
|
||||||
"row": 6,
|
"row": 6,
|
||||||
"col": 0
|
"col": 0
|
||||||
},
|
|
||||||
"f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
|
|
||||||
"sizeX": 7.5,
|
|
||||||
"sizeY": 3,
|
|
||||||
"row": 12,
|
|
||||||
"col": 0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gridSettings": {
|
"gridSettings": {
|
||||||
@ -1257,16 +1198,6 @@
|
|||||||
"stateEntityParamName": null,
|
"stateEntityParamName": null,
|
||||||
"defaultStateEntity": null
|
"defaultStateEntity": null
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"ce27a9d0-93bf-b7a4-054d-d0369a8cf813": {
|
|
||||||
"id": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
|
|
||||||
"alias": "Thermostat-alarm",
|
|
||||||
"filter": {
|
|
||||||
"type": "entityName",
|
|
||||||
"resolveMultiple": false,
|
|
||||||
"entityType": "ASSET",
|
|
||||||
"entityNameFilter": "Thermostat Alarms"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"timewindow": {
|
"timewindow": {
|
||||||
@ -1301,7 +1232,8 @@
|
|||||||
"showDashboardTimewindow": true,
|
"showDashboardTimewindow": true,
|
||||||
"showDashboardExport": true,
|
"showDashboardExport": true,
|
||||||
"toolbarAlwaysOpen": true
|
"toolbarAlwaysOpen": true
|
||||||
}
|
},
|
||||||
|
"filters": {}
|
||||||
},
|
},
|
||||||
"name": "Thermostats"
|
"name": "Thermostats"
|
||||||
}
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"ruleChain": {
|
"ruleChain": {
|
||||||
"additionalInfo": null,
|
"additionalInfo": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"name": "Thermostat Alarms",
|
"name": "Thermostat Alarms",
|
||||||
"firstRuleNodeId": null,
|
"firstRuleNodeId": null,
|
||||||
"root": false,
|
"root": false,
|
||||||
@ -8,131 +10,126 @@
|
|||||||
"configuration": null
|
"configuration": null
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"firstNodeIndex": 5,
|
"firstNodeIndex": 6,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 929,
|
"layoutX": 822,
|
||||||
"layoutY": 67
|
"layoutY": 294
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||||
"name": "Create Temp Alarm",
|
"name": "Save Timeseries",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"alarmType": "High Temperature",
|
"defaultTTL": 0
|
||||||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;",
|
|
||||||
"severity": "MAJOR",
|
|
||||||
"propagate": true,
|
|
||||||
"useMessageAlarmData": false,
|
|
||||||
"relationTypes": [
|
|
||||||
"ToAlarmPropagationAsset"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 930,
|
"description": null,
|
||||||
"layoutY": 201
|
"layoutX": 824,
|
||||||
|
"layoutY": 221
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
|
||||||
"name": "Clear Temp Alarm",
|
"name": "Save Client Attributes",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"alarmType": "High Temperature",
|
"scope": "SERVER_SCOPE",
|
||||||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
|
"notifyDevice": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 930,
|
"layoutX": 494,
|
||||||
"layoutY": 131
|
"layoutY": 309
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
|
||||||
"name": "Create Humidity Alarm",
|
"name": "Message Type Switch",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"alarmType": "Low Humidity",
|
"version": 0
|
||||||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;",
|
|
||||||
"severity": "MINOR",
|
|
||||||
"propagate": true,
|
|
||||||
"useMessageAlarmData": false,
|
|
||||||
"relationTypes": [
|
|
||||||
"ToAlarmPropagationAsset"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 929,
|
"layoutX": 824,
|
||||||
"layoutY": 275
|
"layoutY": 383
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
|
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
"name": "Clear Humidity Alarm",
|
"name": "Log RPC from Device",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"alarmType": "Low Humidity",
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||||
"alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 586,
|
"layoutX": 823,
|
||||||
"layoutY": 148
|
"layoutY": 444
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode",
|
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||||
"name": "Check Alarms",
|
"name": "Log Other",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"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;"
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"additionalInfo": {
|
"additionalInfo": {
|
||||||
"layoutX": 321,
|
"layoutX": 822,
|
||||||
"layoutY": 149
|
"layoutY": 507
|
||||||
},
|
},
|
||||||
"type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode",
|
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
|
||||||
"name": "Fetch Configuration",
|
"name": "RPC Call Request",
|
||||||
"debugMode": false,
|
"debugMode": false,
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"clientAttributeNames": [],
|
"timeoutInSeconds": 60
|
||||||
"sharedAttributeNames": [],
|
}
|
||||||
"serverAttributeNames": [
|
},
|
||||||
"alarmTemperature",
|
{
|
||||||
"thresholdTemperature",
|
"additionalInfo": {
|
||||||
"alarmHumidity",
|
"description": "",
|
||||||
"thresholdHumidity"
|
"layoutX": 209,
|
||||||
],
|
"layoutY": 307
|
||||||
"latestTsKeyNames": [],
|
},
|
||||||
"tellFailureIfAbsent": false,
|
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
|
||||||
"getLatestValueWithTs": false
|
"name": "Device Profile Node",
|
||||||
|
"debugMode": false,
|
||||||
|
"configuration": {
|
||||||
|
"persistAlarmRulesState": false,
|
||||||
|
"fetchAlarmRulesStateOnStart": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connections": [
|
"connections": [
|
||||||
{
|
{
|
||||||
"fromIndex": 4,
|
"fromIndex": 2,
|
||||||
"toIndex": 0,
|
|
||||||
"type": "NewTempAlarm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fromIndex": 4,
|
|
||||||
"toIndex": 1,
|
|
||||||
"type": "ClearTempAlarm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fromIndex": 4,
|
|
||||||
"toIndex": 2,
|
|
||||||
"type": "NewHumidityAlarm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fromIndex": 4,
|
|
||||||
"toIndex": 3,
|
|
||||||
"type": "ClearHumidityAlarm"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fromIndex": 5,
|
|
||||||
"toIndex": 4,
|
"toIndex": 4,
|
||||||
|
"type": "Other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 1,
|
||||||
|
"type": "Post attributes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 0,
|
||||||
|
"type": "Post telemetry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 3,
|
||||||
|
"type": "RPC Request from Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 2,
|
||||||
|
"toIndex": 5,
|
||||||
|
"type": "RPC Request to Device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fromIndex": 6,
|
||||||
|
"toIndex": 2,
|
||||||
"type": "Success"
|
"type": "Success"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer;
|
|||||||
import org.thingsboard.server.common.data.DataConstants;
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
|
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
|
||||||
|
import org.thingsboard.server.common.data.DeviceProfileType;
|
||||||
|
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.TenantProfile;
|
import org.thingsboard.server.common.data.TenantProfile;
|
||||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
|
||||||
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.asset.Asset;
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
import org.thingsboard.server.common.data.id.DeviceProfileId;
|
||||||
@ -42,19 +51,29 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
|||||||
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
|
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
|
||||||
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
|
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
|
||||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
|
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.DynamicValue;
|
||||||
|
import org.thingsboard.server.common.data.query.DynamicValueSourceType;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKeyValueType;
|
||||||
|
import org.thingsboard.server.common.data.query.FilterPredicateValue;
|
||||||
|
import org.thingsboard.server.common.data.query.KeyFilter;
|
||||||
|
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||||
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
|
||||||
import org.thingsboard.server.common.data.widget.WidgetsBundle;
|
import org.thingsboard.server.common.data.widget.WidgetsBundle;
|
||||||
import org.thingsboard.server.dao.asset.AssetService;
|
|
||||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||||
import org.thingsboard.server.dao.customer.CustomerService;
|
import org.thingsboard.server.dao.customer.CustomerService;
|
||||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
||||||
import org.thingsboard.server.dao.device.DeviceProfileService;
|
import org.thingsboard.server.dao.device.DeviceProfileService;
|
||||||
import org.thingsboard.server.dao.device.DeviceService;
|
import org.thingsboard.server.dao.device.DeviceService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||||
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
import org.thingsboard.server.dao.tenant.TenantProfileService;
|
||||||
import org.thingsboard.server.dao.tenant.TenantService;
|
import org.thingsboard.server.dao.tenant.TenantService;
|
||||||
@ -62,6 +81,8 @@ import org.thingsboard.server.dao.user.UserService;
|
|||||||
import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
import org.thingsboard.server.dao.widget.WidgetsBundleService;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Profile("install")
|
@Profile("install")
|
||||||
@ -96,12 +117,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private CustomerService customerService;
|
private CustomerService customerService;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RelationService relationService;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AssetService assetService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private DeviceService deviceService;
|
private DeviceService deviceService;
|
||||||
|
|
||||||
@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DeviceCredentialsService deviceCredentialsService;
|
private DeviceCredentialsService deviceCredentialsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RuleChainService ruleChainService;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
protected BCryptPasswordEncoder passwordEncoder() {
|
protected BCryptPasswordEncoder passwordEncoder() {
|
||||||
return new BCryptPasswordEncoder();
|
return new BCryptPasswordEncoder();
|
||||||
@ -245,19 +263,133 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
|
|||||||
createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
|
createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
|
||||||
"Raspberry Pi GPIO control sample application");
|
"Raspberry Pi GPIO control sample application");
|
||||||
|
|
||||||
Asset thermostatAlarms = new Asset();
|
DeviceProfile thermostatDeviceProfile = new DeviceProfile();
|
||||||
thermostatAlarms.setTenantId(demoTenant.getId());
|
thermostatDeviceProfile.setTenantId(demoTenant.getId());
|
||||||
thermostatAlarms.setName("Thermostat Alarms");
|
thermostatDeviceProfile.setDefault(false);
|
||||||
thermostatAlarms.setType("AlarmPropagationAsset");
|
thermostatDeviceProfile.setName("thermostat");
|
||||||
thermostatAlarms = assetService.saveAsset(thermostatAlarms);
|
thermostatDeviceProfile.setType(DeviceProfileType.DEFAULT);
|
||||||
|
thermostatDeviceProfile.setTransportType(DeviceTransportType.DEFAULT);
|
||||||
|
thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
|
||||||
|
thermostatDeviceProfile.setDescription("Thermostat device profile");
|
||||||
|
thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains(
|
||||||
|
demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId());
|
||||||
|
|
||||||
DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat");
|
DeviceProfileData deviceProfileData = new DeviceProfileData();
|
||||||
|
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
|
||||||
|
DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration();
|
||||||
|
DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null);
|
||||||
|
deviceProfileData.setConfiguration(configuration);
|
||||||
|
deviceProfileData.setTransportConfiguration(transportConfiguration);
|
||||||
|
deviceProfileData.setProvisionConfiguration(provisionConfiguration);
|
||||||
|
thermostatDeviceProfile.setProfileData(deviceProfileData);
|
||||||
|
|
||||||
DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
|
DeviceProfileAlarm highTemperature = new DeviceProfileAlarm();
|
||||||
DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
|
highTemperature.setId("highTemperatureAlarmID");
|
||||||
|
highTemperature.setAlarmType("High Temperature");
|
||||||
|
AlarmRule temperatureRule = new AlarmRule();
|
||||||
|
AlarmCondition temperatureCondition = new AlarmCondition();
|
||||||
|
temperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset"));
|
KeyFilter alarmTemperatureAttributeFilter = new KeyFilter();
|
||||||
relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset"));
|
alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature"));
|
||||||
|
alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
|
BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate();
|
||||||
|
alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
|
alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||||
|
alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate);
|
||||||
|
|
||||||
|
KeyFilter temperatureTimeseriesFilter = new KeyFilter();
|
||||||
|
temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||||
|
temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
|
temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
|
FilterPredicateValue<Double> temperatureTimeseriesPredicateValue =
|
||||||
|
new FilterPredicateValue<>(25.0, null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
|
||||||
|
temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue);
|
||||||
|
temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate);
|
||||||
|
temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter));
|
||||||
|
temperatureRule.setAlarmDetails("Current temperature = ${temperature}");
|
||||||
|
temperatureRule.setCondition(temperatureCondition);
|
||||||
|
highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule)));
|
||||||
|
|
||||||
|
AlarmRule clearTemperatureRule = new AlarmRule();
|
||||||
|
AlarmCondition clearTemperatureCondition = new AlarmCondition();
|
||||||
|
clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
|
KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter();
|
||||||
|
clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
|
||||||
|
clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
|
clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL);
|
||||||
|
FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue =
|
||||||
|
new FilterPredicateValue<>(25.0, null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature"));
|
||||||
|
|
||||||
|
clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue);
|
||||||
|
clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate);
|
||||||
|
clearTemperatureCondition.setCondition(Collections.singletonList(clearTemperatureTimeseriesFilter));
|
||||||
|
clearTemperatureRule.setCondition(clearTemperatureCondition);
|
||||||
|
clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}");
|
||||||
|
highTemperature.setClearRule(clearTemperatureRule);
|
||||||
|
|
||||||
|
DeviceProfileAlarm lowHumidity = new DeviceProfileAlarm();
|
||||||
|
lowHumidity.setId("lowHumidityAlarmID");
|
||||||
|
lowHumidity.setAlarmType("Low Humidity");
|
||||||
|
AlarmRule humidityRule = new AlarmRule();
|
||||||
|
AlarmCondition humidityCondition = new AlarmCondition();
|
||||||
|
humidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
|
KeyFilter alarmHumidityAttributeFilter = new KeyFilter();
|
||||||
|
alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity"));
|
||||||
|
alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN);
|
||||||
|
BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate();
|
||||||
|
alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||||
|
alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE));
|
||||||
|
alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate);
|
||||||
|
|
||||||
|
KeyFilter humidityTimeseriesFilter = new KeyFilter();
|
||||||
|
humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
||||||
|
humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
|
humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS);
|
||||||
|
FilterPredicateValue<Double> humidityTimeseriesPredicateValue =
|
||||||
|
new FilterPredicateValue<>(60.0, null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
|
||||||
|
humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue);
|
||||||
|
humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate);
|
||||||
|
humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter));
|
||||||
|
|
||||||
|
humidityRule.setCondition(humidityCondition);
|
||||||
|
humidityRule.setAlarmDetails("Current humidity = ${humidity}");
|
||||||
|
lowHumidity.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule)));
|
||||||
|
|
||||||
|
AlarmRule clearHumidityRule = new AlarmRule();
|
||||||
|
AlarmCondition clearHumidityCondition = new AlarmCondition();
|
||||||
|
clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||||
|
|
||||||
|
KeyFilter clearHumidityTimeseriesFilter = new KeyFilter();
|
||||||
|
clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity"));
|
||||||
|
clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC);
|
||||||
|
NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate();
|
||||||
|
clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL);
|
||||||
|
FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue =
|
||||||
|
new FilterPredicateValue<>(60.0, null,
|
||||||
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity"));
|
||||||
|
|
||||||
|
clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue);
|
||||||
|
clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate);
|
||||||
|
clearHumidityCondition.setCondition(Collections.singletonList(clearHumidityTimeseriesFilter));
|
||||||
|
clearHumidityRule.setCondition(clearHumidityCondition);
|
||||||
|
clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}");
|
||||||
|
lowHumidity.setClearRule(clearHumidityRule);
|
||||||
|
|
||||||
|
deviceProfileData.setAlarms(Arrays.asList(highTemperature, lowHumidity));
|
||||||
|
|
||||||
|
DeviceProfile savedThermostatDeviceProfile = deviceProfileService.saveDeviceProfile(thermostatDeviceProfile);
|
||||||
|
|
||||||
|
DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
|
||||||
|
DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
|
||||||
|
|
||||||
attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE,
|
attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE,
|
||||||
Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
|
Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)),
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2020 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.transport.mqtt.util;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AlwaysTrueTopicFilter implements MqttTopicFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean filter(String topic) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,7 +33,9 @@ public class MqttTopicFilterFactory {
|
|||||||
throw new IllegalArgumentException("Topic filter can't be empty!");
|
throw new IllegalArgumentException("Topic filter can't be empty!");
|
||||||
}
|
}
|
||||||
return filters.computeIfAbsent(topicFilter, filter -> {
|
return filters.computeIfAbsent(topicFilter, filter -> {
|
||||||
if (filter.contains("+") || filter.contains("#")) {
|
if (filter.equals("#")) {
|
||||||
|
return new AlwaysTrueTopicFilter();
|
||||||
|
} else if (filter.contains("+") || filter.contains("#")) {
|
||||||
String regex = filter
|
String regex = filter
|
||||||
.replace("\\", "\\\\")
|
.replace("\\", "\\\\")
|
||||||
.replace("+", "[^/]+")
|
.replace("+", "[^/]+")
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@ -31,6 +30,9 @@ public class MqttTopicFilterFactoryTest {
|
|||||||
private static String TEST_STR_1 = "Sensor/Temperature/House/48";
|
private static String TEST_STR_1 = "Sensor/Temperature/House/48";
|
||||||
private static String TEST_STR_2 = "Sensor/Temperature";
|
private static String TEST_STR_2 = "Sensor/Temperature";
|
||||||
private static String TEST_STR_3 = "Sensor/Temperature2/House/48";
|
private static String TEST_STR_3 = "Sensor/Temperature2/House/48";
|
||||||
|
private static String TEST_STR_4 = "/Sensor/Temperature2/House/48";
|
||||||
|
private static String TEST_STR_5 = "Sensor/ Temperature";
|
||||||
|
private static String TEST_STR_6 = "/";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void metadataCanBeUpdated() throws ScriptException {
|
public void metadataCanBeUpdated() throws ScriptException {
|
||||||
@ -51,6 +53,17 @@ public class MqttTopicFilterFactoryTest {
|
|||||||
assertTrue(filter.filter(TEST_STR_1));
|
assertTrue(filter.filter(TEST_STR_1));
|
||||||
assertTrue(filter.filter(TEST_STR_2));
|
assertTrue(filter.filter(TEST_STR_2));
|
||||||
assertFalse(filter.filter(TEST_STR_3));
|
assertFalse(filter.filter(TEST_STR_3));
|
||||||
|
|
||||||
|
filter = MqttTopicFilterFactory.toFilter("#");
|
||||||
|
assertTrue(filter.filter(TEST_STR_1));
|
||||||
|
assertTrue(filter.filter(TEST_STR_2));
|
||||||
|
assertTrue(filter.filter(TEST_STR_3));
|
||||||
|
assertTrue(filter.filter(TEST_STR_4));
|
||||||
|
assertTrue(filter.filter(TEST_STR_5));
|
||||||
|
assertTrue(filter.filter(TEST_STR_6));
|
||||||
|
|
||||||
|
filter = MqttTopicFilterFactory.toFilter("Sensor/Temperature#");
|
||||||
|
assertFalse(filter.filter(TEST_STR_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright © 2016-2020 The Thingsboard Authors
|
* Copyright © 2016-2020 The Thingsboard Authors
|
||||||
*
|
* <p>
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
* <p>
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
* <p>
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -33,6 +33,7 @@ import org.springframework.cache.Cache;
|
|||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.DeviceProfile;
|
import org.thingsboard.server.common.data.DeviceProfile;
|
||||||
import org.thingsboard.server.common.data.DeviceProfileInfo;
|
import org.thingsboard.server.common.data.DeviceProfileInfo;
|
||||||
@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.DeviceTransportType;
|
|||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
|
||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
|
||||||
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
|
import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
|
||||||
@ -60,8 +62,10 @@ import org.thingsboard.server.dao.tenant.TenantDao;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE;
|
import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE;
|
||||||
import static org.thingsboard.server.dao.service.Validator.validateId;
|
import static org.thingsboard.server.dao.service.Validator.validateId;
|
||||||
@ -347,6 +351,22 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms();
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(profileAlarms)) {
|
||||||
|
Set<String> alarmTypes = new HashSet<>();
|
||||||
|
for (DeviceProfileAlarm alarm : profileAlarms) {
|
||||||
|
String alarmType = alarm.getAlarmType();
|
||||||
|
if (StringUtils.isEmpty(alarmType)) {
|
||||||
|
throw new DataValidationException("Alarm rule type should be specified!");
|
||||||
|
}
|
||||||
|
if (!alarmTypes.add(alarmType)) {
|
||||||
|
throw new DataValidationException(String.format("Can't create device profile with the same alarm rule types: \"%s\"!", alarmType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -393,6 +413,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
|
|||||||
" for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!");
|
" for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) {
|
private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) {
|
||||||
if (!isEmptySettings) {
|
if (!isEmptySettings) {
|
||||||
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage);
|
throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage);
|
||||||
|
|||||||
@ -41,6 +41,9 @@
|
|||||||
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
|
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
|
||||||
{{ 'device-profile.alarm-type-required' | translate }}
|
{{ 'device-profile.alarm-type-required' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('unique')">
|
||||||
|
{{ 'device-profile.alarm-type-unique' | translate }}
|
||||||
|
</mat-error>
|
||||||
<mat-hint *ngIf="!disabled"
|
<mat-hint *ngIf="!disabled"
|
||||||
innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
|
innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|||||||
@ -133,6 +133,19 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public validate(c: FormControl) {
|
public validate(c: FormControl) {
|
||||||
|
if (c.parent) {
|
||||||
|
const alarmType = c.value.alarmType;
|
||||||
|
const profileAlarmsType = [];
|
||||||
|
c.parent.getRawValue().forEach((alarm: DeviceProfileAlarm) => {
|
||||||
|
profileAlarmsType.push(alarm.alarmType);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (profileAlarmsType.filter(profileAlarmType => profileAlarmType === alarmType).length > 1) {
|
||||||
|
this.alarmFormGroup.get('alarmType').setErrors({
|
||||||
|
unique: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return (this.alarmFormGroup.valid) ? null : {
|
return (this.alarmFormGroup.valid) ? null : {
|
||||||
alarm: {
|
alarm: {
|
||||||
valid: false,
|
valid: false,
|
||||||
|
|||||||
@ -38,7 +38,7 @@ import {
|
|||||||
} from '@shared/models/device.models';
|
} from '@shared/models/device.models';
|
||||||
import { EntityType } from '@shared/models/entity-type.models';
|
import { EntityType } from '@shared/models/entity-type.models';
|
||||||
import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
||||||
import {ServiceType} from "@shared/models/queue.models";
|
import { ServiceType } from '@shared/models/queue.models';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-device-profile',
|
selector: 'tb-device-profile',
|
||||||
@ -176,10 +176,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
|
|||||||
transportConfiguration: entity.profileData?.transportConfiguration,
|
transportConfiguration: entity.profileData?.transportConfiguration,
|
||||||
alarms: entity.profileData?.alarms,
|
alarms: entity.profileData?.alarms,
|
||||||
provisionConfiguration: deviceProvisionConfiguration
|
provisionConfiguration: deviceProvisionConfiguration
|
||||||
}});
|
}}, {emitEvent: false});
|
||||||
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null});
|
this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
|
||||||
this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName});
|
this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
|
||||||
this.entityForm.patchValue({description: entity.description});
|
this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareFormValue(formValue: any): any {
|
prepareFormValue(formValue: any): any {
|
||||||
|
|||||||
@ -913,6 +913,7 @@
|
|||||||
"edit-alarm-rule": "Edit alarm rule",
|
"edit-alarm-rule": "Edit alarm rule",
|
||||||
"alarm-type": "Alarm type",
|
"alarm-type": "Alarm type",
|
||||||
"alarm-type-required": "Alarm type is required.",
|
"alarm-type-required": "Alarm type is required.",
|
||||||
|
"alarm-type-unique": "Alarm type must be unique within the device profile alarm rules.",
|
||||||
"alarm-type-pattern-hint": "Alarm type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata",
|
"alarm-type-pattern-hint": "Alarm type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata",
|
||||||
"create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
|
"create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
|
||||||
"create-alarm-rules": "Create alarm rules",
|
"create-alarm-rules": "Create alarm rules",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user