diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index 7328dc7fb3..9f2ac9c178 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -319,7 +319,7 @@ "resources": [], "templateHtml": "\n", "templateCss": "", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1\r\n }\r\n}\r\n\r\nself.onResize = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-resize', self.ctx.$scope.formId);\r\n}\r\n", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n var scope = self.ctx.$scope;\r\n var id = self.ctx.$scope.$injector.get('utils').guid();\r\n scope.formId = \"form-\"+id;\r\n scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n\r\nself.onResize = function() {\r\n self.ctx.$scope.$broadcast('multiple-input-resize', self.ctx.$scope.formId);\r\n}\r\n", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"MultipleInput\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showActionButtons\":{\n \"title\":\"Show action buttons\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\": true\n },\n \"fieldsAlignment\": {\n \"title\": \"Fields alignment\",\n \"type\": \"string\",\n \"default\": \"row\"\n },\n \"fieldsInRow\": {\n \"title\": \"Number of fields in the row\",\n \"type\": \"number\",\n \"default\": \"2\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showActionButtons\",\n \"showResultMessage\",\n {\n \"key\": \"fieldsAlignment\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"row\",\n \"label\": \"Row (default)\"\n },\n {\n \"value\": \"column\",\n \"label\": \"Column\"\n }\n ]\n },\n \"fieldsInRow\"\n ]\n}", "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"dataKeyType\": {\n \"title\": \"Datakey type\",\n \"type\": \"string\",\n \"default\": \"server\"\n },\n \"dataKeyValueType\": {\n \"title\": \"Datakey value type\",\n \"type\": \"string\",\n \"default\": \"string\"\n },\n \"required\": {\n \"title\": \"Value is required\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"isEditable\": {\n \"title\": \"Ability to edit attribute\",\n \"type\": \"string\",\n \"default\": \"editable\"\n },\n \"disabledOnDataKey\": {\n \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"dataKeyHidden\": {\n \"title\": \"Hide input field\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"step\": {\n \"title\": \"Step interval between values (only for numbers)\",\n \"type\": \"number\",\n \"default\": \"1\"\n },\n \"requiredErrorMessage\": {\n \"title\": \"'Required' error message\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"icon\": {\n \"title\": \"Icon to show before input cell\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"dataKeyType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"server\",\n \"label\": \"Server attribute (default)\"\n },\n {\n \"value\": \"shared\",\n \"label\": \"Shared attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Timeseries\"\n }\n ]\n },\n {\n \"key\": \"dataKeyValueType\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"string\",\n \"label\": \"String\"\n },\n {\n \"value\": \"double\",\n \"label\": \"Double\"\n },\n {\n \"value\": \"integer\",\n \"label\": \"Integer\"\n },\n {\n \"value\": \"booleanCheckbox\",\n \"label\": \"Boolean (Checkbox)\"\n },\n {\n \"value\": \"booleanSwitch\",\n \"label\": \"Boolean (Switch)\"\n },\n {\n \"value\": \"dateTime\",\n \"label\": \"Date & Time\"\n },\n {\n \"value\": \"date\",\n \"label\": \"Date\"\n },\n {\n \"value\": \"time\",\n \"label\": \"Time\"\n }\n ]\n },\n \"required\",\n {\n \"key\": \"isEditable\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"editable\",\n \"label\": \"Editable (default)\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n },\n {\n \"value\": \"readonly\",\n \"label\": \"Read-only\"\n }\n ]\n },\n \"disabledOnDataKey\",\n \"dataKeyHidden\",\n \"step\",\n \"requiredErrorMessage\",\n\t\t{\n \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n ]\n}\n", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" diff --git a/ui/src/app/widget/lib/multiple-input-widget.js b/ui/src/app/widget/lib/multiple-input-widget.js index 1993a01aa4..64bb6b7a81 100644 --- a/ui/src/app/widget/lib/multiple-input-widget.js +++ b/ui/src/app/widget/lib/multiple-input-widget.js @@ -47,14 +47,14 @@ function MultipleInputWidgetController($q, $scope, $translate, attributeService, vm.entityDetected = false; vm.isAllParametersValid = true; - vm.data = []; + vm.sources = []; vm.datasources = null; vm.discardAll = discardAll; vm.inputChanged = inputChanged; vm.save = save; - $scope.$watch('vm.ctx', function() { + $scope.$watch('vm.ctx', function () { if (vm.ctx && vm.ctx.defaultSubscription) { vm.settings = vm.ctx.settings; vm.widgetConfig = vm.ctx.widgetConfig; @@ -65,100 +65,113 @@ function MultipleInputWidgetController($q, $scope, $translate, attributeService, } }); - $scope.$on('multiple-input-data-updated', function(event, formId) { + $scope.$on('multiple-input-data-updated', function (event, formId) { if (vm.formId == formId) { updateWidgetData(vm.subscription.data); $scope.$digest(); } }); - $scope.$on('multiple-input-resize', function(event, formId) { + $scope.$on('multiple-input-resize', function (event, formId) { if (vm.formId == formId) { updateWidgetDisplaying(); } }); function discardAll() { - for (var i = 0; i < vm.data.length; i++) { - vm.data[i].data.currentValue = vm.data[i].data.originalValue; + for (var i = 0; i < vm.sources.length; i++) { + for (var j = 0; j < vm.sources[i].keys.length; j++) { + vm.sources[i].keys[j].data.currentValue = vm.sources[i].keys[j].data.originalValue; + } } $scope.multipleInputForm.$setPristine(); } - function inputChanged(key) { + function inputChanged(source, key) { if (!vm.settings.showActionButtons) { if (!key.settings.required || (key.settings.required && key.data && angular.isDefined(key.data.currentValue))) { - vm.save(key); + var dataToSave = { + datasource: source.datasource, + keys: [key] + }; + vm.save(dataToSave); } } } - function save(key) { + function save(dataToSave) { var tasks = []; - var serverAttributes = [], sharedAttributes = [], telemetry = []; var config = { ignoreLoading: !vm.settings.showActionButtons }; var data; - if (key) { - data = [key]; + if (dataToSave) { + data = [dataToSave]; } else { - data = vm.data; + data = vm.sources; } for (let i = 0; i < data.length; i++) { - var item = data[i]; - if (item.data.currentValue !== item.data.originalValue) { - var attribute = { - key: item.name - }; - switch (item.settings.dataKeyValueType) { - case 'dateTime': - case 'date': - attribute.value = item.data.currentValue.getTime(); - break; - case 'time': - attribute.value = item.data.currentValue.getTime() - moment().startOf('day').valueOf();//eslint-disable-line - break; - default: - attribute.value = item.data.currentValue; - } + var serverAttributes = [], sharedAttributes = [], telemetry = []; + for (let j = 0; j < data[i].keys.length; j++) { + var key = data[i].keys[j]; + if (key.data.currentValue !== key.data.originalValue) { + var attribute = { + key: key.name + }; + if (key.data.currentValue) { + switch (key.settings.dataKeyValueType) { + case 'dateTime': + case 'date': + attribute.value = key.data.currentValue.getTime(); + break; + case 'time': + attribute.value = key.data.currentValue.getTime() - moment().startOf('day').valueOf();//eslint-disable-line + break; + default: + attribute.value = key.data.currentValue; + } + } else { + attribute.value = key.data.currentValue; + } - switch (item.settings.dataKeyType) { - case 'shared': - sharedAttributes.push(attribute); - break; - case 'timeseries': - telemetry.push(attribute); - break; - default: - serverAttributes.push(attribute); + switch (key.settings.dataKeyType) { + case 'shared': + sharedAttributes.push(attribute); + break; + case 'timeseries': + telemetry.push(attribute); + break; + default: + serverAttributes.push(attribute); + } } } + if (serverAttributes.length) { + tasks.push(attributeService.saveEntityAttributes( + data[i].datasource.entityType, + data[i].datasource.entityId, + types.attributesScope.server.value, + serverAttributes, + config)); + } + if (sharedAttributes.length) { + tasks.push(attributeService.saveEntityAttributes( + data[i].datasource.entityType, + data[i].datasource.entityId, + types.attributesScope.shared.value, + sharedAttributes, + config)); + } + if (telemetry.length) { + tasks.push(attributeService.saveEntityTimeseries( + data[i].datasource.entityType, + data[i].datasource.entityId, + types.latestTelemetry.value, + telemetry, + config)); + } } - for (let i = 0; i < serverAttributes.length; i++) { - tasks.push(attributeService.saveEntityAttributes( - vm.datasources[0].entityType, - vm.datasources[0].entityId, - types.attributesScope.server.value, - serverAttributes, - config)); - } - for (let i = 0; i < sharedAttributes.length; i++) { - tasks.push(attributeService.saveEntityAttributes( - vm.datasources[0].entityType, - vm.datasources[0].entityId, - types.attributesScope.shared.value, - sharedAttributes, - config)); - } - for (let i = 0; i < telemetry.length; i++) { - tasks.push(attributeService.saveEntityTimeseries( - vm.datasources[0].entityType, - vm.datasources[0].entityId, - types.latestTelemetry.value, - telemetry, - config)); - } + if (tasks.length) { $q.all(tasks).then( function success() { @@ -173,6 +186,8 @@ function MultipleInputWidgetController($q, $scope, $translate, attributeService, } } ); + } else { + $scope.multipleInputForm.$setPristine(); } } @@ -186,6 +201,18 @@ function MultipleInputWidgetController($q, $scope, $translate, attributeService, vm.ctx.widgetTitle = vm.widgetTitle; + //For backward compatibility + if (angular.isUndefined(vm.settings.showActionButtons)) { + vm.settings.showActionButtons = true; + } + if (angular.isUndefined(vm.settings.fieldsAlignment)) { + vm.settings.fieldsAlignment = 'row'; + } + if (angular.isUndefined(vm.settings.fieldsInRow)) { + vm.settings.fieldsInRow = 2; + } + //For backward compatibility + vm.isVerticalAlignment = !(vm.settings.fieldsAlignment === 'row'); if (!vm.isVerticalAlignment && vm.settings.fieldsInRow) { @@ -195,60 +222,105 @@ function MultipleInputWidgetController($q, $scope, $translate, attributeService, function updateDatasources() { if (vm.datasources && vm.datasources.length) { - var datasource = vm.datasources[0]; - if (datasource.type === types.datasourceType.entity) { - for (var i = 0; i < datasource.dataKeys.length; i++) { - if ((datasource.entityType !== types.entityType.device) && (datasource.dataKeys[i].settings.dataKeyType == 'shared')) { - vm.isAllParametersValid = false; + vm.entityDetected = true; + for (var i = 0; i < vm.datasources.length; i++) { + var datasource = vm.datasources[i]; + var source = { + datasource: datasource, + keys: [] + }; + if (datasource.type === types.datasourceType.entity) { + for (var j = 0; j < datasource.dataKeys.length; j++) { + if ((datasource.entityType !== types.entityType.device) && (datasource.dataKeys[j].settings.dataKeyType == 'shared')) { + vm.isAllParametersValid = false; + } + source.keys.push(datasource.dataKeys[j]); + if (source.keys[j].units) { + source.keys[j].label += ' (' + source.keys[j].units + ')'; + } + source.keys[j].data = {}; + + //For backward compatibility + if (angular.isUndefined(source.keys[j].settings.dataKeyType)) { + if (vm.settings.attributesShared === true) { + source.keys[j].settings.dataKeyType = 'shared'; + } else { + source.keys[j].settings.dataKeyType = 'server'; + } + } + + if (angular.isUndefined(source.keys[j].settings.dataKeyValueType)) { + if (source.keys[j].settings.inputTypeNumber === true) { + source.keys[j].settings.dataKeyValueType = 'double'; + } else { + source.keys[j].settings.dataKeyValueType = 'string'; + } + } + + if (angular.isUndefined(source.keys[j].settings.isEditable)) { + if (source.keys[j].settings.readOnly === true) { + source.keys[j].settings.isEditable = 'readonly'; + } else { + source.keys[j].settings.isEditable = 'editable'; + } + } + //For backward compatibility + } - vm.data.push(datasource.dataKeys[i]); - vm.data[i].data = {}; + } else { + vm.entityDetected = false; } - vm.entityDetected = true; + vm.sources.push(source); } } } function updateWidgetData(data) { - for (var i = 0; i < vm.data.length; i++) { - var keyData = data[i].data; - if (keyData && keyData.length) { - var value; - switch (vm.data[i].settings.dataKeyValueType) { - case 'dateTime': - case 'date': - value = moment(keyData[0][1]).toDate(); // eslint-disable-line - break; - case 'time': - value = moment().startOf('day').add(keyData[0][1], 'ms').toDate(); // eslint-disable-line - break; - case 'booleanCheckbox': - case 'booleanSwitch': - value = (keyData[0][1] === 'true'); - break; - default: - value = keyData[0][1]; + var dataIndex = 0; + for (var i = 0; i < vm.sources.length; i++) { + var source = vm.sources[i]; + for (var j = 0; j < source.keys.length; j++) { + var keyData = data[dataIndex].data; + var key = source.keys[j]; + if (keyData && keyData.length) { + var value; + switch (key.settings.dataKeyValueType) { + case 'dateTime': + case 'date': + value = moment(keyData[0][1]).toDate(); // eslint-disable-line + break; + case 'time': + value = moment().startOf('day').add(keyData[0][1], 'ms').toDate(); // eslint-disable-line + break; + case 'booleanCheckbox': + case 'booleanSwitch': + value = (keyData[0][1] === 'true'); + break; + default: + value = keyData[0][1]; + } + + key.data = { + currentValue: value, + originalValue: value + }; } - vm.data[i].data = { - currentValue: value, - originalValue: value - }; - } - - if (vm.data[i].settings.isEditable === 'editable' && vm.data[i].settings.disabledOnDataKey) { - var conditions = data.filter((item) => { - return item.dataKey.name === vm.data[i].settings.disabledOnDataKey; - }); - if (conditions && conditions.length) { - if (conditions[0].data.length) { - if (conditions[0].data[0][1] === 'false') { - vm.data[i].settings.disabledOnCondition = true; - } else { - vm.data[i].settings.disabledOnCondition = !conditions[0].data[0][1]; + if (key.settings.isEditable === 'editable' && key.settings.disabledOnDataKey) { + var conditions = data.filter((item) => { + return item.dataKey.name === key.settings.disabledOnDataKey; + }); + if (conditions && conditions.length) { + if (conditions[0].data.length) { + if (conditions[0].data[0][1] === 'false') { + key.settings.disabledOnCondition = true; + } else { + key.settings.disabledOnCondition = !conditions[0].data[0][1]; + } } } } + dataIndex++; } } } diff --git a/ui/src/app/widget/lib/multiple-input-widget.tpl.html b/ui/src/app/widget/lib/multiple-input-widget.tpl.html index e375af49c0..3240d97c1b 100644 --- a/ui/src/app/widget/lib/multiple-input-widget.tpl.html +++ b/ui/src/app/widget/lib/multiple-input-widget.tpl.html @@ -17,9 +17,9 @@ -->
-
-
-
+
+
+
@@ -32,13 +32,13 @@ ng-required="key.settings.required" type="text" md-select-on-focus - ng-blur="vm.inputChanged(key)"> + ng-blur="vm.inputChanged(source,key)">
{{ key.settings.requiredErrorMessage }}
-
+
@@ -52,13 +52,13 @@ type="number" step="key.settings.step" md-select-on-focus - ng-blur="vm.inputChanged(key)"> + ng-blur="vm.inputChanged(source,key)">
{{ key.settings.requiredErrorMessage }}
-
+
@@ -73,26 +73,26 @@ step="key.settings.step" md-select-on-focus ng-pattern="/^-?[0-9]+$/" - ng-blur="vm.inputChanged(key)"> + ng-blur="vm.inputChanged(source,key)">
{{ key.settings.requiredErrorMessage }}
value.invalid-integer-value
-
+
+ ng-change="vm.inputChanged(source,key)"> {{key.label}}
-
+
{{key.label}} @@ -100,7 +100,7 @@
@@ -108,7 +108,7 @@ ng-if="key.settings.dataKeyValueType !== 'time'" mdp-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" ng-model="key.data.currentValue" - ng-change="vm.inputChanged(key)" + ng-change="vm.inputChanged(source,key)" mdp-placeholder="{{ 'widgets.input-widgets.date' | translate }}">
{{ key.settings.requiredErrorMessage }}
@@ -118,7 +118,7 @@ ng-if="key.settings.dataKeyValueType !== 'date'" mdp-disabled="key.settings.isEditable === 'disabled' || key.settings.disabledOnCondition" ng-model="key.data.currentValue" - ng-change="vm.inputChanged(key)" + ng-change="vm.inputChanged(source,key)" mdp-placeholder="{{ 'widgets.input-widgets.time' | translate }}" mdp-auto-switch="true">