Merge branch 'master' into develop/2.3

This commit is contained in:
Igor Kulikov 2019-02-01 15:20:39 +02:00
commit ded035b927
6 changed files with 199 additions and 7 deletions

View File

@ -63,7 +63,7 @@
"resources": [], "resources": [],
"templateHtml": "<form class=\"attribute-update-form\"\n name=\"attrUpdateForm\"\n ng-submit=\"updateAttribute($event)\"\n>\n <div style=\"padding: 0 8px; margin: auto 0;\">\n <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n <div class=\"grid__element\">\n <md-checkbox ng-model=\"checkboxValue\"\n aria-label=\"Switch entity attribute value\"\n ng-change=\"changed()\"\n ng-true-value=\"'true'\"\n ng-false-value=\"'false'\"\n >\n {{currentValue}}\n </md-checkbox>\n </div>\n </div>\n\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\" ng-bind=\"message\"></div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n No attribute is selected\n </div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n Timeseries parameter cannot be used in this widget\n </div>\n </div>\n</form>", "templateHtml": "<form class=\"attribute-update-form\"\n name=\"attrUpdateForm\"\n ng-submit=\"updateAttribute($event)\"\n>\n <div style=\"padding: 0 8px; margin: auto 0;\">\n <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n <div class=\"grid__element\">\n <md-checkbox ng-model=\"checkboxValue\"\n aria-label=\"Switch entity attribute value\"\n ng-change=\"changed()\"\n ng-true-value=\"'true'\"\n ng-false-value=\"'false'\"\n >\n {{currentValue}}\n </md-checkbox>\n </div>\n </div>\n\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\" ng-bind=\"message\"></div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n No attribute is selected\n </div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n Timeseries parameter cannot be used in this widget\n </div>\n </div>\n</form>",
"templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.entity-title {\n font-weight: bold;\n font-size: 22px;\n padding-top: 12px;\n padding-bottom: 6px;\n color: #666;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .md-icon-button md-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n\nmd-toast{\n min-width: 0;\n}\nmd-toast .md-toast-content {\n font-size: 14px!important;\n}", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.entity-title {\n font-weight: bold;\n font-size: 22px;\n padding-top: 12px;\n padding-bottom: 6px;\n color: #666;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .md-icon-button md-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n\nmd-toast{\n min-width: 0;\n}\nmd-toast .md-toast-content {\n font-size: 14px!important;\n}",
"controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet toast;\nlet utils;\nlet types;\nlet map;\nlet mapReverse;\n\nself.onInit = function() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get('attributeService');\n toast = $scope.$injector.get('toast');\n utils = $scope.$injector.get('utils');\n types = $scope.$injector.get('types');\n settings = angular.copy(self.ctx.settings) || {};\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = 'No entity selected';\n\n settings.trueValue = settings.trueValue || true;\n settings.falseValue = settings.falseValue || false;\n \n map = {\"true\":settings.trueValue, \"false\": settings.falseValue};\n mapReverse = {[settings.trueValue]:\"true\", [settings.falseValue]:\"false\"};\n $scope.checkboxValue = \"false\";\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.changed = function () {\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n }\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type != \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entityType,\n datasource.entityId,\n types.attributesScope.server.value,\n [\n {\n key: $scope.currentKey,\n value: $scope.currentValue\n }\n ]\n ).then(\n function success() {\n $scope.originalValue = $scope.currentValue;\n if (settings.showResultMessage) {\n toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = mapReverse[$scope.originalValue = self.ctx.data[0].data[0][1]] || 'false';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.$digest();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n }\n}\n\nself.onDestroy = function() {\n\n}\n", "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet toast;\nlet utils;\nlet types;\nlet map;\nlet mapReverse;\n\nself.onInit = function() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get('attributeService');\n toast = $scope.$injector.get('toast');\n utils = $scope.$injector.get('utils');\n types = $scope.$injector.get('types');\n settings = angular.copy(self.ctx.settings) || {};\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = 'No entity selected';\n\n settings.trueValue = settings.trueValue || true;\n settings.falseValue = settings.falseValue || false;\n\n map = {\"true\":settings.trueValue, \"false\": settings.falseValue};\n mapReverse = {[settings.trueValue]:true, [settings.falseValue]:false};\n $scope.checkboxValue = \"false\";\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.changed = function () {\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n }\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type != \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entityType,\n datasource.entityId,\n types.attributesScope.server.value,\n [\n {\n key: $scope.currentKey,\n value: mapReverse[$scope.currentValue] || false\n }\n ]\n ).then(\n function success() {\n $scope.originalValue = $scope.currentValue;\n if (settings.showResultMessage) {\n toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = ($scope.originalValue = self.ctx.data[0].data[0][1]) || false;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.$digest();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n }\n}\n\nself.onResize = function() {}\nself.onDestroy = function() {}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"trueValue\": {\n \"title\": \"True value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"falseValue\": {\n \"title\": \"False value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"trueValue\",\n \"falseValue\"\n ]\n}", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"trueValue\": {\n \"title\": \"True value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"falseValue\": {\n \"title\": \"False value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"trueValue\",\n \"falseValue\"\n ]\n}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
@ -127,7 +127,7 @@
"resources": [], "resources": [],
"templateHtml": "<form class=\"attribute-update-form\"\n name=\"attrUpdateForm\"\n ng-submit=\"updateAttribute($event)\"\n>\n <div style=\"padding: 0 8px; margin: auto 0;\">\n <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n <div class=\"grid__element\">\n <md-checkbox ng-model=\"checkboxValue\"\n aria-label=\"Switch entity attribute value\"\n ng-change=\"changed()\"\n ng-true-value=\"'true'\"\n ng-false-value=\"'false'\"\n >\n {{currentValue}}\n </md-checkbox>\n </div>\n </div>\n\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\" ng-bind=\"message\"></div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n No attribute is selected\n </div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n Timeseries parameter cannot be used in this widget\n </div>\n </div>\n</form>", "templateHtml": "<form class=\"attribute-update-form\"\n name=\"attrUpdateForm\"\n ng-submit=\"updateAttribute($event)\"\n>\n <div style=\"padding: 0 8px; margin: auto 0;\">\n <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n <div class=\"grid__element\">\n <md-checkbox ng-model=\"checkboxValue\"\n aria-label=\"Switch entity attribute value\"\n ng-change=\"changed()\"\n ng-true-value=\"'true'\"\n ng-false-value=\"'false'\"\n >\n {{currentValue}}\n </md-checkbox>\n </div>\n </div>\n\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\" ng-bind=\"message\"></div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n No attribute is selected\n </div>\n <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n Timeseries parameter cannot be used in this widget\n </div>\n </div>\n</form>",
"templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.entity-title {\n font-weight: bold;\n font-size: 22px;\n padding-top: 12px;\n padding-bottom: 6px;\n color: #666;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .md-icon-button md-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n\nmd-toast{\n min-width: 0;\n}\nmd-toast .md-toast-content {\n font-size: 14px!important;\n}", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.entity-title {\n font-weight: bold;\n font-size: 22px;\n padding-top: 12px;\n padding-bottom: 6px;\n color: #666;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .md-button.md-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .md-icon-button md-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n\nmd-toast{\n min-width: 0;\n}\nmd-toast .md-toast-content {\n font-size: 14px!important;\n}",
"controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet toast;\nlet utils;\nlet types;\nlet map;\nlet mapReverse;\n\nself.onInit = function() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get('attributeService');\n toast = $scope.$injector.get('toast');\n utils = $scope.$injector.get('utils');\n types = $scope.$injector.get('types');\n settings = angular.copy(self.ctx.settings) || {};\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = 'No entity selected';\n\n settings.trueValue = settings.trueValue || true;\n settings.falseValue = settings.falseValue || false;\n \n map = {\"true\":settings.trueValue, \"false\": settings.falseValue};\n mapReverse = {[settings.trueValue]:\"true\", [settings.falseValue]:\"false\"};\n $scope.checkboxValue = \"false\";\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.changed = function () {\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n }\n \n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === \"DEVICE\") {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n } else {\n $scope.message = 'Selected entity cannot have shared attributes';\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type != \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entityType,\n datasource.entityId,\n types.attributesScope.shared.value,\n [\n {\n key: $scope.currentKey,\n value: $scope.currentValue\n }\n ]\n ).then(\n function success() {\n $scope.originalValue = $scope.currentValue;\n if (settings.showResultMessage) {\n toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n\n try {\n $scope.checkboxValue = mapReverse[$scope.originalValue = self.ctx.data[0].data[0][1]] || 'false';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.$digest();\n\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n }\n}\n\nself.onDestroy = function() {\n\n}\n", "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet toast;\nlet utils;\nlet types;\nlet map;\nlet mapReverse;\n\nself.onInit = function() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get('attributeService');\n toast = $scope.$injector.get('toast');\n utils = $scope.$injector.get('utils');\n types = $scope.$injector.get('types');\n settings = angular.copy(self.ctx.settings) || {};\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = 'No entity selected';\n\n settings.trueValue = settings.trueValue || true;\n settings.falseValue = settings.falseValue || false;\n\n map = {\"true\":settings.trueValue, \"false\": settings.falseValue};\n mapReverse = {[settings.trueValue]:true, [settings.falseValue]:false};\n $scope.checkboxValue = \"false\";\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.changed = function () {\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n }\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === \"DEVICE\") {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n } else {\n $scope.message = 'Selected entity cannot have shared attributes';\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type != \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entityType,\n datasource.entityId,\n types.attributesScope.shared.value,\n [\n {\n key: $scope.currentKey,\n value: mapReverse[$scope.currentValue] || false\n }\n ]\n ).then(\n function success() {\n $scope.originalValue = $scope.currentValue;\n if (settings.showResultMessage) {\n toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n $scope.checkboxValue = ($scope.originalValue = self.ctx.data[0].data[0][1]) || 'false';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.$digest();\n\n } catch (e) {\n console.log(e);\n }\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1\n }\n}\n\nself.onResize = function() {}\nself.onDestroy = function() {}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"trueValue\": {\n \"title\": \"True value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"falseValue\": {\n \"title\": \"False value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"trueValue\",\n \"falseValue\"\n ]\n}", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"trueValue\": {\n \"title\": \"True value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"falseValue\": {\n \"title\": \"False value\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showResultMessage\":{\n \"title\":\"Show result message\",\n \"type\":\"boolean\",\n \"default\":true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n \"showResultMessage\",\n \"trueValue\",\n \"falseValue\"\n ]\n}",
"dataKeySettingsSchema": "{}\n", "dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"trueValue\":\"active\",\"falseValue\":\"inactive\"},\"title\":\"Update shared boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"trueValue\":\"active\",\"falseValue\":\"inactive\"},\"title\":\"Update shared boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"

View File

@ -0,0 +1,134 @@
/**
* Copyright © 2016-2018 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.rule.engine.filter;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import java.util.List;
import java.util.Map;
@Slf4j
@RuleNode(
type = ComponentType.FILTER,
name = "check existence fields",
relationTypes = {"True", "False"},
configClazz = TbCheckMessageNodeConfiguration.class,
nodeDescription = "Checks the existence of the selected keys from message data and metadata.",
nodeDetails = "If selected checkbox 'Check that all selected keys are present'\" and all keys in message data and metadata are exist - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.\n" +
"Else if the checkbox is not selected, and at least one of the keys from data or metadata of the message exists - send Message via <b>True</b> chain, otherwise, <b>False</b> chain is used. ",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckMessageConfig")
public class TbCheckMessageNode implements TbNode {
private static final Gson gson = new Gson();
private TbCheckMessageNodeConfiguration config;
private List<String> messageNamesList;
private List<String> metadataNamesList;
@Override
public void init(TbContext tbContext, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbCheckMessageNodeConfiguration.class);
messageNamesList = config.getMessageNames();
metadataNamesList = config.getMetadataNames();
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
try {
if (config.isCheckAllKeys()) {
ctx.tellNext(msg, allKeysData(msg) && allKeysMetadata(msg) ? "True" : "False");
} else {
ctx.tellNext(msg, atLeastOneData(msg) || atLeastOneMetadata(msg) ? "True" : "False");
}
} catch (Exception e) {
ctx.tellFailure(msg, e);
}
}
@Override
public void destroy() {
}
private boolean allKeysData(TbMsg msg) {
if (!messageNamesList.isEmpty()) {
Map<String, String> dataMap = dataToMap(msg);
return processAllKeys(messageNamesList, dataMap);
}
return true;
}
private boolean allKeysMetadata(TbMsg msg) {
if (!metadataNamesList.isEmpty()) {
Map<String, String> metadataMap = metadataToMap(msg);
return processAllKeys(metadataNamesList, metadataMap);
}
return true;
}
private boolean atLeastOneData(TbMsg msg) {
if (!messageNamesList.isEmpty()) {
Map<String, String> dataMap = dataToMap(msg);
return processAtLeastOne(messageNamesList, dataMap);
}
return false;
}
private boolean atLeastOneMetadata(TbMsg msg) {
if (!metadataNamesList.isEmpty()) {
Map<String, String> metadataMap = metadataToMap(msg);
return processAtLeastOne(metadataNamesList, metadataMap);
}
return false;
}
private boolean processAllKeys(List<String> data, Map<String, String> map) {
for (String field : data) {
if (!map.containsKey(field)) {
return false;
}
}
return true;
}
private boolean processAtLeastOne(List<String> data, Map<String, String> map) {
for (String field : data) {
if (map.containsKey(field)) {
return true;
}
}
return false;
}
private Map<String, String> metadataToMap(TbMsg msg) {
return msg.getMetaData().getData();
}
@SuppressWarnings("unchecked")
private Map<String, String> dataToMap(TbMsg msg) {
return (Map<String, String>) gson.fromJson(msg.getData(), Map.class);
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2018 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.rule.engine.filter;
import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import java.util.Collections;
import java.util.List;
@Data
public class TbCheckMessageNodeConfiguration implements NodeConfiguration {
private List<String> messageNames;
private List<String> metadataNames;
private boolean checkAllKeys;
@Override
public TbCheckMessageNodeConfiguration defaultConfiguration() {
TbCheckMessageNodeConfiguration configuration = new TbCheckMessageNodeConfiguration();
configuration.setMessageNames(Collections.emptyList());
configuration.setMetadataNames(Collections.emptyList());
configuration.setCheckAllKeys(true);
return configuration;
}
}

View File

@ -53,6 +53,19 @@
return result; return result;
}; };
} }
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
if (angular.isNumber(start)) {
start = 0;
}
if (start + search.length > this.length) {
return false;
} else {
return this.indexOf(search, start) !== -1;
}
};
}
(function (arr) { (function (arr) {
arr.forEach(function (item) { arr.forEach(function (item) {

View File

@ -539,10 +539,14 @@ export default class TbMapWidgetV2 {
} }
} }
if (locationsChanged && tbMap.initBounds) { if (locationsChanged && tbMap.initBounds) {
tbMap.initBounds = !datasources.every( let dataReceived = datasources.every(
function (ds) { function (ds) {
return ds.dataReceived === true; return ds.dataReceived === true;
}); });
let dataValid = dataReceived && dataMap.dsDataMap.every(function (ds) {
return !(!ds[tbMap.locationSettings.latKeyName] && !ds[tbMap.locationSettings.lngKeyName]);
});
tbMap.initBounds = !dataValid;
tbMap.map.fitBounds(bounds); tbMap.map.fitBounds(bounds);
} }
} }