merge with master
This commit is contained in:
		
						commit
						dc310f4cab
					
				@ -219,8 +219,8 @@
 | 
			
		||||
            "defaultPageSize": 10,
 | 
			
		||||
            "defaultSortOrder": "-createdTime",
 | 
			
		||||
            "enableSelectColumnDisplay": false,
 | 
			
		||||
            "enableStatusFilter": true,
 | 
			
		||||
            "alarmsTitle": "Alarms"
 | 
			
		||||
            "alarmsTitle": "Alarms",
 | 
			
		||||
            "enableFilter": true
 | 
			
		||||
          },
 | 
			
		||||
          "title": "New Alarms table",
 | 
			
		||||
          "dropShadow": true,
 | 
			
		||||
@ -234,6 +234,9 @@
 | 
			
		||||
          "showLegend": false,
 | 
			
		||||
          "alarmSource": {
 | 
			
		||||
            "type": "entity",
 | 
			
		||||
            "name": "alarms",
 | 
			
		||||
            "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e",
 | 
			
		||||
            "filterId": null,
 | 
			
		||||
            "dataKeys": [
 | 
			
		||||
              {
 | 
			
		||||
                "name": "createdTime",
 | 
			
		||||
@ -275,9 +278,7 @@
 | 
			
		||||
                "settings": {},
 | 
			
		||||
                "_hash": 0.7977920750136249
 | 
			
		||||
              }
 | 
			
		||||
            ],
 | 
			
		||||
            "entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813",
 | 
			
		||||
            "name": "alarms"
 | 
			
		||||
            ]
 | 
			
		||||
          },
 | 
			
		||||
          "alarmSearchStatus": "ANY",
 | 
			
		||||
          "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;",
 | 
			
		||||
            "useLabelFunction": true,
 | 
			
		||||
            "provider": "openstreet-map",
 | 
			
		||||
            "draggableMarker": true
 | 
			
		||||
            "draggableMarker": true,
 | 
			
		||||
            "editablePolygon": true
 | 
			
		||||
          },
 | 
			
		||||
          "title": "New Markers Placement - OpenStreetMap",
 | 
			
		||||
          "dropShadow": true,
 | 
			
		||||
@ -1062,61 +1064,6 @@
 | 
			
		||||
          "displayTimewindow": true
 | 
			
		||||
        },
 | 
			
		||||
        "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": {
 | 
			
		||||
@ -1215,12 +1162,6 @@
 | 
			
		||||
                "sizeY": 6,
 | 
			
		||||
                "row": 6,
 | 
			
		||||
                "col": 0
 | 
			
		||||
              },
 | 
			
		||||
              "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": {
 | 
			
		||||
                "sizeX": 7.5,
 | 
			
		||||
                "sizeY": 3,
 | 
			
		||||
                "row": 12,
 | 
			
		||||
                "col": 0
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            "gridSettings": {
 | 
			
		||||
@ -1257,16 +1198,6 @@
 | 
			
		||||
          "stateEntityParamName": 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": {
 | 
			
		||||
@ -1301,7 +1232,8 @@
 | 
			
		||||
      "showDashboardTimewindow": true,
 | 
			
		||||
      "showDashboardExport": true,
 | 
			
		||||
      "toolbarAlwaysOpen": true
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    "filters": {}
 | 
			
		||||
  },
 | 
			
		||||
  "name": "Thermostats"
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "ruleChain": {
 | 
			
		||||
    "additionalInfo": null,
 | 
			
		||||
    "additionalInfo": {
 | 
			
		||||
      "description": ""
 | 
			
		||||
    },
 | 
			
		||||
    "name": "Thermostat Alarms",
 | 
			
		||||
    "firstRuleNodeId": null,
 | 
			
		||||
    "root": false,
 | 
			
		||||
@ -8,131 +10,126 @@
 | 
			
		||||
    "configuration": null
 | 
			
		||||
  },
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "firstNodeIndex": 5,
 | 
			
		||||
    "firstNodeIndex": 6,
 | 
			
		||||
    "nodes": [
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 929,
 | 
			
		||||
          "layoutY": 67
 | 
			
		||||
          "layoutX": 822,
 | 
			
		||||
          "layoutY": 294
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
 | 
			
		||||
        "name": "Create Temp Alarm",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
 | 
			
		||||
        "name": "Save Timeseries",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "alarmType": "High Temperature",
 | 
			
		||||
          "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"
 | 
			
		||||
          ]
 | 
			
		||||
          "defaultTTL": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 930,
 | 
			
		||||
          "layoutY": 201
 | 
			
		||||
          "description": null,
 | 
			
		||||
          "layoutX": 824,
 | 
			
		||||
          "layoutY": 221
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
 | 
			
		||||
        "name": "Clear Temp Alarm",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
 | 
			
		||||
        "name": "Save Client Attributes",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "alarmType": "High Temperature",
 | 
			
		||||
          "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n    details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
 | 
			
		||||
          "scope": "SERVER_SCOPE",
 | 
			
		||||
          "notifyDevice": null
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 930,
 | 
			
		||||
          "layoutY": 131
 | 
			
		||||
          "layoutX": 494,
 | 
			
		||||
          "layoutY": 309
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode",
 | 
			
		||||
        "name": "Create Humidity Alarm",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
 | 
			
		||||
        "name": "Message Type Switch",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "alarmType": "Low Humidity",
 | 
			
		||||
          "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"
 | 
			
		||||
          ]
 | 
			
		||||
          "version": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 929,
 | 
			
		||||
          "layoutY": 275
 | 
			
		||||
          "layoutX": 824,
 | 
			
		||||
          "layoutY": 383
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode",
 | 
			
		||||
        "name": "Clear Humidity Alarm",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log RPC from Device",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "alarmType": "Low Humidity",
 | 
			
		||||
          "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n    details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;"
 | 
			
		||||
          "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "layoutX": 586,
 | 
			
		||||
          "layoutY": 148
 | 
			
		||||
          "layoutX": 823,
 | 
			
		||||
          "layoutY": 444
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode",
 | 
			
		||||
        "name": "Check Alarms",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.action.TbLogNode",
 | 
			
		||||
        "name": "Log Other",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "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": {
 | 
			
		||||
          "layoutX": 321,
 | 
			
		||||
          "layoutY": 149
 | 
			
		||||
          "layoutX": 822,
 | 
			
		||||
          "layoutY": 507
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode",
 | 
			
		||||
        "name": "Fetch Configuration",
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
 | 
			
		||||
        "name": "RPC Call Request",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "clientAttributeNames": [],
 | 
			
		||||
          "sharedAttributeNames": [],
 | 
			
		||||
          "serverAttributeNames": [
 | 
			
		||||
            "alarmTemperature",
 | 
			
		||||
            "thresholdTemperature",
 | 
			
		||||
            "alarmHumidity",
 | 
			
		||||
            "thresholdHumidity"
 | 
			
		||||
          ],
 | 
			
		||||
          "latestTsKeyNames": [],
 | 
			
		||||
          "tellFailureIfAbsent": false,
 | 
			
		||||
          "getLatestValueWithTs": false
 | 
			
		||||
          "timeoutInSeconds": 60
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "additionalInfo": {
 | 
			
		||||
          "description": "",
 | 
			
		||||
          "layoutX": 209,
 | 
			
		||||
          "layoutY": 307
 | 
			
		||||
        },
 | 
			
		||||
        "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
 | 
			
		||||
        "name": "Device Profile Node",
 | 
			
		||||
        "debugMode": false,
 | 
			
		||||
        "configuration": {
 | 
			
		||||
          "persistAlarmRulesState": false,
 | 
			
		||||
          "fetchAlarmRulesStateOnStart": false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "connections": [
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 4,
 | 
			
		||||
        "toIndex": 0,
 | 
			
		||||
        "type": "NewTempAlarm"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 4,
 | 
			
		||||
        "toIndex": 1,
 | 
			
		||||
        "type": "ClearTempAlarm"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 4,
 | 
			
		||||
        "toIndex": 2,
 | 
			
		||||
        "type": "NewHumidityAlarm"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 4,
 | 
			
		||||
        "toIndex": 3,
 | 
			
		||||
        "type": "ClearHumidityAlarm"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "fromIndex": 5,
 | 
			
		||||
        "fromIndex": 2,
 | 
			
		||||
        "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"
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer;
 | 
			
		||||
import org.thingsboard.server.common.data.DataConstants;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
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.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.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.DeviceId;
 | 
			
		||||
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.DoubleDataEntry;
 | 
			
		||||
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.DeviceCredentials;
 | 
			
		||||
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.dao.asset.AssetService;
 | 
			
		||||
import org.thingsboard.server.dao.attributes.AttributesService;
 | 
			
		||||
import org.thingsboard.server.dao.customer.CustomerService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceProfileService;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceService;
 | 
			
		||||
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.tenant.TenantProfileService;
 | 
			
		||||
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 java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@Profile("install")
 | 
			
		||||
@ -96,12 +117,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private CustomerService customerService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private RelationService relationService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private AssetService assetService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DeviceService deviceService;
 | 
			
		||||
 | 
			
		||||
@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DeviceCredentialsService deviceCredentialsService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private RuleChainService ruleChainService;
 | 
			
		||||
 | 
			
		||||
    @Bean
 | 
			
		||||
    protected BCryptPasswordEncoder passwordEncoder() {
 | 
			
		||||
        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 " +
 | 
			
		||||
                "Raspberry Pi GPIO control sample application");
 | 
			
		||||
 | 
			
		||||
        Asset thermostatAlarms = new Asset();
 | 
			
		||||
        thermostatAlarms.setTenantId(demoTenant.getId());
 | 
			
		||||
        thermostatAlarms.setName("Thermostat Alarms");
 | 
			
		||||
        thermostatAlarms.setType("AlarmPropagationAsset");
 | 
			
		||||
        thermostatAlarms = assetService.saveAsset(thermostatAlarms);
 | 
			
		||||
        DeviceProfile thermostatDeviceProfile = new DeviceProfile();
 | 
			
		||||
        thermostatDeviceProfile.setTenantId(demoTenant.getId());
 | 
			
		||||
        thermostatDeviceProfile.setDefault(false);
 | 
			
		||||
        thermostatDeviceProfile.setName("thermostat");
 | 
			
		||||
        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();
 | 
			
		||||
        DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
 | 
			
		||||
        DeviceProfileAlarm highTemperature = new DeviceProfileAlarm();
 | 
			
		||||
        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"));
 | 
			
		||||
        relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset"));
 | 
			
		||||
        KeyFilter alarmTemperatureAttributeFilter = new KeyFilter();
 | 
			
		||||
        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,
 | 
			
		||||
                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!");
 | 
			
		||||
        }
 | 
			
		||||
        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
 | 
			
		||||
                        .replace("\\", "\\\\")
 | 
			
		||||
                        .replace("+", "[^/]+")
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,6 @@ import org.junit.runner.RunWith;
 | 
			
		||||
import org.mockito.runners.MockitoJUnitRunner;
 | 
			
		||||
 | 
			
		||||
import javax.script.ScriptException;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import static org.junit.Assert.assertFalse;
 | 
			
		||||
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_2 = "Sensor/Temperature";
 | 
			
		||||
    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
 | 
			
		||||
    public void metadataCanBeUpdated() throws ScriptException {
 | 
			
		||||
@ -51,6 +53,17 @@ public class MqttTopicFilterFactoryTest {
 | 
			
		||||
        assertTrue(filter.filter(TEST_STR_1));
 | 
			
		||||
        assertTrue(filter.filter(TEST_STR_2));
 | 
			
		||||
        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
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 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
 | 
			
		||||
 *
 | 
			
		||||
 * <p>
 | 
			
		||||
 * http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 * <p>
 | 
			
		||||
 * 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.
 | 
			
		||||
@ -33,6 +33,7 @@ import org.springframework.cache.Cache;
 | 
			
		||||
import org.springframework.cache.CacheManager;
 | 
			
		||||
import org.springframework.cache.annotation.Cacheable;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.util.CollectionUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.Device;
 | 
			
		||||
import org.thingsboard.server.common.data.DeviceProfile;
 | 
			
		||||
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.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.DeviceProfileTransportConfiguration;
 | 
			
		||||
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.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
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.dao.service.Validator.validateId;
 | 
			
		||||
@ -136,7 +140,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
 | 
			
		||||
            if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) {
 | 
			
		||||
                throw new DataValidationException("Device profile with such name already exists!");
 | 
			
		||||
            } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) {
 | 
			
		||||
                    throw new DataValidationException("Device profile with such provision device key already exists!");
 | 
			
		||||
                throw new DataValidationException("Device profile with such provision device key already exists!");
 | 
			
		||||
            } else {
 | 
			
		||||
                throw t;
 | 
			
		||||
            }
 | 
			
		||||
@ -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
 | 
			
		||||
@ -393,6 +413,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
 | 
			
		||||
                                " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) {
 | 
			
		||||
                    if (!isEmptySettings) {
 | 
			
		||||
                        throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage);
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,9 @@
 | 
			
		||||
      <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
 | 
			
		||||
        {{ 'device-profile.alarm-type-required' | translate }}
 | 
			
		||||
      </mat-error>
 | 
			
		||||
      <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('unique')">
 | 
			
		||||
        {{ 'device-profile.alarm-type-unique' | translate }}
 | 
			
		||||
      </mat-error>
 | 
			
		||||
      <mat-hint *ngIf="!disabled"
 | 
			
		||||
                innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
 | 
			
		||||
    </mat-form-field>
 | 
			
		||||
 | 
			
		||||
@ -133,6 +133,19 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 : {
 | 
			
		||||
      alarm: {
 | 
			
		||||
        valid: false,
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,7 @@ import {
 | 
			
		||||
} from '@shared/models/device.models';
 | 
			
		||||
import { EntityType } from '@shared/models/entity-type.models';
 | 
			
		||||
import { RuleChainId } from '@shared/models/id/rule-chain-id';
 | 
			
		||||
import {ServiceType} from "@shared/models/queue.models";
 | 
			
		||||
import { ServiceType } from '@shared/models/queue.models';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-device-profile',
 | 
			
		||||
@ -176,10 +176,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
 | 
			
		||||
      transportConfiguration: entity.profileData?.transportConfiguration,
 | 
			
		||||
      alarms: entity.profileData?.alarms,
 | 
			
		||||
      provisionConfiguration: deviceProvisionConfiguration
 | 
			
		||||
    }});
 | 
			
		||||
    this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null});
 | 
			
		||||
    this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName});
 | 
			
		||||
    this.entityForm.patchValue({description: entity.description});
 | 
			
		||||
    }}, {emitEvent: false});
 | 
			
		||||
    this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false});
 | 
			
		||||
    this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false});
 | 
			
		||||
    this.entityForm.patchValue({description: entity.description}, {emitEvent: false});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  prepareFormValue(formValue: any): any {
 | 
			
		||||
 | 
			
		||||
@ -913,6 +913,7 @@
 | 
			
		||||
        "edit-alarm-rule": "Edit alarm rule",
 | 
			
		||||
        "alarm-type": "Alarm type",
 | 
			
		||||
        "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",
 | 
			
		||||
        "create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm",
 | 
			
		||||
        "create-alarm-rules": "Create alarm rules",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user