{
  "widgetsBundle": {
    "alias": "input_widgets",
    "title": "Input widgets",
    "image": null
  },
  "widgetTypes": [
    {
      "alias": "update_server_string_attribute",
      "name": "Update server string attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3.5,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           maxlength=\"{{settings.maxLength}}\"\n                           minlength=\"{{settings.minLength}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n        \n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity selected\n        </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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\n}\n\nmd-toast{\n    min-width: 0;\n}\nmd-toast .md-toast-content {\n    font-size: 14px!important;\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\r\n    $scope = self.ctx.$scope;\r\n    attributeService = $scope.$injector.get('attributeService');\r\n    toast = $scope.$injector.get('toast');\r\n    utils = $scope.$injector.get('utils');\r\n    types = $scope.$injector.get('types');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\r\n    $scope.labelValue = settings.labelValue || \"Value\";\r\n\r\n    if (self.ctx.datasources && self.ctx.datasources.length) {\r\n        var datasource = self.ctx.datasources[0];\r\n        if (datasource.type === 'entity') {\r\n            if (datasource.entityType && datasource.entityId) {\r\n                $scope.entityName = datasource.entityName;\r\n                if (settings.widgetTitle && settings.widgetTitle.length) {\r\n                    $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n                } else {\r\n                    $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n                }\r\n\r\n                $scope.entityDetected = true;\r\n            }\r\n        }\r\n        if (datasource.dataKeys.length) {\r\n            if (datasource.dataKeys[0].type != \"attribute\") {\r\n                $scope.isValidParameter = false;\r\n            } else {\r\n                $scope.currentKey = datasource.dataKeys[0].name;\r\n                $scope.dataKeyType = datasource.dataKeys[0].type;\r\n                $scope.dataKeyDetected = true;\r\n            }\r\n        }\r\n    }\r\n\r\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n    $scope.updateAttribute = function () {\r\n        if ($scope.entityDetected) {\r\n            var datasource = self.ctx.datasources[0];\r\n\r\n            attributeService.saveEntityAttributes(\r\n                datasource.entityType,\r\n                datasource.entityId,\r\n                types.attributesScope.server.value,\r\n                [\r\n                    {\r\n                        key: $scope.currentKey,\r\n                        value: $scope.currentValue\r\n                    }\r\n                ]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalValue = $scope.currentValue;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    };\r\n\r\n    $scope.changeFocus = function () {\r\n        if ($scope.currentValue === $scope.originalValue) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            if (!$scope.isFocused) {\r\n                $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\r\n                $scope.$digest();\r\n            }\r\n        }\r\n\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 1,\r\n        dataKeysOptional: true\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxLength\": {\n                \"title\": \"Max length\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minLength\": {\n                \"title\": \"Min length\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxLength\",\n        \"minLength\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\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 server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_server_integer_attribute",
      "name": "Update server integer attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity selected\n        </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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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;\n\nself.onInit = function() {\n\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 = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        if ($scope.dataKeyDetected) {\n            if (!$scope.isFocused) {\n                $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n                correctValue($scope.currentValue);\n                $scope.$digest();\n            }\n        }\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nfunction correctValue(value) {\n    if (typeof value !== \"number\") {\n        $scope.currentValue = 0;\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_server_double_attribute",
      "name": "Update server double attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity 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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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;\n\nself.onInit = function() {\n\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 = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\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[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\n\n        if (!$scope.isFocused) {\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n            correctValue($scope.currentValue);\n            $scope.$digest();\n        }\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nfunction correctValue(value) {\n    if (typeof value !== \"number\") {\n        $scope.currentValue = 0;\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_server_boolean_attribute",
      "name": "Update server boolean attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\">\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 && !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}",
        "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.message = 'No entity selected';\n\n    $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n    $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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",
        "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\":{}}"
      }
    },
    {
      "alias": "update_shared_string_attribute",
      "name": "Update shared string attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3.5,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           maxlength=\"{{settings.maxLength}}\"\n                           minlength=\"{{settings.minLength}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update shared attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update shared attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\n             ng-hide=\"entityDetected\"\n             ng-bind=\"message\"\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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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;\n\nself.onInit = function() {\n\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 = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.message = 'No entity selected';\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\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[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\n\n        if (!$scope.isFocused) {\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n            $scope.$digest();\n        }\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        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxLength\": {\n                \"title\": \"Max length\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minLength\": {\n                \"title\": \"Min length\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxLength\",\n        \"minLength\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\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 shared string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_shared_integer_attribute",
      "name": "Update shared integer attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update shared attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update shared attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\n             ng-hide=\"entityDetected\"\n             ng-bind=\"message\"\n        >\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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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;\n\nself.onInit = function() {\n\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 = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\n    $scope.message = 'No entity selected';\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 share attribute';\n            }\n        }\n        if (datasource.dataKeys[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\n\n        if (!$scope.isFocused) {\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n            correctValue($scope.currentValue);\n            $scope.$digest();\n        }\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nfunction correctValue(value) {\n    if (typeof value !== \"number\") {\n        $scope.currentValue = 0;\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 shared integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_shared_double_attribute",
      "name": "Update shared double attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update shared attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update shared attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\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 && !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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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;\n\nself.onInit = function() {\n\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 = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity attribute is required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\n    $scope.message = 'No entity selected';\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[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\n\n        if (!$scope.isFocused) {\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n            correctValue($scope.currentValue);\n            $scope.$digest();\n        }\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nfunction correctValue(value) {\n    if (typeof value !== \"number\") {\n        $scope.currentValue = 0;\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 shared double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_shared_boolean_attribute",
      "name": "Update shared boolean attribute",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\">\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 && !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}",
        "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.message = 'No entity selected';\n\n    $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n    $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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[0].type != \"attribute\") {\n            $scope.isValidParameter = false;\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.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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",
        "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\":{}}"
      }
    },
    {
      "alias": "update_string_timeseries",
      "name": "Update string timeseries",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           maxlength=\"{{settings.maxLength}}\"\n                           minlength=\"{{settings.minLength}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n        \n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity selected\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            Attribute 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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\n}\n\nmd-toast{\n    min-width: 0;\n}\nmd-toast .md-toast-content {\n    font-size: 14px!important;\n}",
        "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\nlet $q\r\nlet $http;\r\n\r\nself.onInit = function() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    attributeService = $scope.$injector.get('attributeService');\r\n    toast = $scope.$injector.get('toast');\r\n    utils = $scope.$injector.get('utils');\r\n    types = $scope.$injector.get('types');\r\n    $q = $scope.$injector.get('$q');\r\n    $http = $scope.$injector.get('$http');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity timeseries are required\";\r\n    $scope.labelValue = settings.labelValue || \"Value\";\r\n\r\n    if (self.ctx.datasources && self.ctx.datasources.length) {\r\n        var datasource = self.ctx.datasources[0];\r\n        if (datasource.type === 'entity') {\r\n            if (datasource.entityType && datasource.entityId) {\r\n                $scope.entityName = datasource.entityName;\r\n                if (settings.widgetTitle && settings.widgetTitle.length) {\r\n                    $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n                } else {\r\n                    $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n                }\r\n\r\n                $scope.entityDetected = true;\r\n            }\r\n        }\r\n        if (datasource.dataKeys[0].type != \"timeseries\") {\r\n            $scope.isValidParameter = false;\r\n        }\r\n    }\r\n\r\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n    $scope.updateAttribute = function () {\r\n        if ($scope.entityDetected) {\r\n            var datasource = self.ctx.datasources[0];\r\n\r\n            saveEntityTimeseries(\r\n                datasource.entityType,\r\n                datasource.entityId,\r\n                [\r\n                    {\r\n                        key: $scope.currentKey,\r\n                        value: $scope.currentValue\r\n                    }\r\n                ]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalValue = $scope.currentValue;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    };\r\n\r\n    $scope.changeFocus = function () {\r\n        if ($scope.currentValue === $scope.originalValue) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n\r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var deferred = $q.defer();\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (angular.isDefined(telemetries[a].value) && telemetries[a].value !== null) {\r\n                telemetriesData[telemetries[a].key] = telemetries[a].value;\r\n            }\r\n        }\r\n        if (Object.keys(telemetriesData).length) {\r\n            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\r\n            $http.post(url, telemetriesData).then(\r\n                function(response) {\r\n                    deferred.resolve(response.data);\r\n                },\r\n                function() {\r\n                    deferred.reject();\r\n                }\r\n            );\r\n        }\r\n        return deferred.promise;\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    try {\r\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\r\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\r\n\r\n        if (!$scope.isFocused) {\r\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\r\n            $scope.$digest();\r\n        }\r\n\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 1,\r\n        dataKeysOptional: true\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxLength\": {\n                \"title\": \"Max length\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minLength\": {\n                \"title\": \"Min length\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxLength\",\n        \"minLength\"\n    ]\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 string timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_boolean_timeseries",
      "name": "Update boolean timeseries",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\">\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 && !isValidParameter\">\n            Attribute parameter cannot be used in this widget\n        </div>\n    </div>\n</form>",
        "templateCss": ".attribute-update-form {\r\n    overflow: hidden;\r\n    height: 100%;\r\n    display: flex;\r\n    flex-direction: column;\r\n}\r\n\r\n.entity-title {\r\n    font-weight: bold;\r\n    font-size: 22px;\r\n    padding-top: 12px;\r\n    padding-bottom: 6px;\r\n    color: #666;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n    display: flex;\r\n}\r\n.grid__element:first-child {\r\n    flex: 1;\r\n}\r\n\r\n.grid__element {\r\n    display: flex;\r\n}\r\n\r\n.attribute-update-form .md-button.md-icon-button {\r\n    margin: 0;\r\n}\r\n\r\n.attribute-update-form .md-button.md-icon-button {\r\n    width: 32px;\r\n    min-width: 32px;\r\n    height: 32px;\r\n    min-height: 32px;\r\n    padding: 0 !important;\r\n    margin: 0 !important;\r\n    line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .md-icon-button md-icon {\r\n    width: 20px;\r\n    min-width: 20px;\r\n    height: 20px;\r\n    min-height: 20px;\r\n    font-size: 20px;\r\n}\r\n\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\nmd-toast .md-toast-content {\r\n    font-size: 14px!important;\r\n}",
        "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet toast;\nlet utils;\nlet types;\nlet $q\nlet $http;\nlet map;\nlet mapReverse;\n\nself.onInit = function() {\n\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    $q = $scope.$injector.get('$q');\n    $http = $scope.$injector.get('$http');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n\n    $scope.message = 'No entity selected';\n\n    $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n    $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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 can not have share attribute';\n            }\n        }\n        if (datasource.dataKeys[0].type != \"timeseries\") {\n            $scope.isValidParameter = false;\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            saveEntityTimeseries(\n                datasource.entityType,\n                datasource.entityId,\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    function saveEntityTimeseries(entityType, entityId, telemetries) {\n        var deferred = $q.defer();\n        var telemetriesData = {};\n        for (var a = 0; a < telemetries.length; a++) {\n            if (angular.isDefined(telemetries[a].value) && telemetries[a].value !== null) {\n                telemetriesData[telemetries[a].key] = telemetries[a].value;\n            }\n        }\n        if (Object.keys(telemetriesData).length) {\n            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n            $http.post(url, telemetriesData).then(\n                function(response) {\n                    deferred.resolve(response.data);\n                },\n                function() {\n                    deferred.reject();\n                }\n            );\n        }\n        return deferred.promise;\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\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        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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",
        "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 boolean timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_double_timeseries",
      "name": "Update double timeseries",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity selected\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            Attribute 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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\n}\n\nmd-toast{\n    min-width: 0;\n}\nmd-toast .md-toast-content {\n    font-size: 14px!important;\n}",
        "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\nlet $q\r\nlet $http;\r\n\r\nself.onInit = function() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    attributeService = $scope.$injector.get('attributeService');\r\n    toast = $scope.$injector.get('toast');\r\n    utils = $scope.$injector.get('utils');\r\n    types = $scope.$injector.get('types');\r\n    $q = $scope.$injector.get('$q');\r\n    $http = $scope.$injector.get('$http');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity timeseries are required\";\r\n    $scope.labelValue = settings.labelValue || \"Value\";\r\n\r\n    if (self.ctx.datasources && self.ctx.datasources.length) {\r\n        var datasource = self.ctx.datasources[0];\r\n        if (datasource.type === 'entity') {\r\n            if (datasource.entityType && datasource.entityId) {\r\n                $scope.entityName = datasource.entityName;\r\n                if (settings.widgetTitle && settings.widgetTitle.length) {\r\n                    $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n                } else {\r\n                    $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n                }\r\n\r\n                $scope.entityDetected = true;\r\n            }\r\n        }\r\n        if (datasource.dataKeys[0].type != \"timeseries\") {\r\n            $scope.isValidParameter = false;\r\n        }\r\n    }\r\n\r\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n    $scope.updateAttribute = function () {\r\n        if ($scope.entityDetected) {\r\n            var datasource = self.ctx.datasources[0];\r\n\r\n            saveEntityTimeseries(\r\n                datasource.entityType,\r\n                datasource.entityId,\r\n                [\r\n                    {\r\n                        key: $scope.currentKey,\r\n                        value: $scope.currentValue\r\n                    }\r\n                ]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalValue = $scope.currentValue;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess('Update successful', 1000, angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError('Update failed', angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    };\r\n\r\n    $scope.changeFocus = function () {\r\n        if ($scope.currentValue === $scope.originalValue) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n\r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var deferred = $q.defer();\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (angular.isDefined(telemetries[a].value) && telemetries[a].value !== null) {\r\n                telemetriesData[telemetries[a].key] = telemetries[a].value;\r\n            }\r\n        }\r\n        if (Object.keys(telemetriesData).length) {\r\n            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\r\n            $http.post(url, telemetriesData).then(\r\n                function(response) {\r\n                    deferred.resolve(response.data);\r\n                },\r\n                function() {\r\n                    deferred.reject();\r\n                }\r\n            );\r\n        }\r\n        return deferred.promise;\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n\r\n    try {\r\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\r\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\r\n\r\n        if (!$scope.isFocused) {\r\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\r\n            correctValue($scope.currentValue);\r\n            $scope.$digest();\r\n        }\r\n\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nfunction correctValue(value) {\r\n    if (typeof value !== \"number\") {\r\n        $scope.currentValue = 0;\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 1,\r\n        dataKeysOptional: true\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 double timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_integer_timeseries",
      "name": "Update integer timeseries",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "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\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter\">\n            <div class=\"grid__element\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{labelValue}}</label>\n                    <input required\n                           name=\"attribute\"\n                           ng-model=\"currentValue\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           max=\"{{settings.maxValue}}\"\n                           min=\"{{settings.minValue}}\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.attribute.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"Update server attribute\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">Update server attribute</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"Discard changes\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">Discard changes</md-tooltip>\n                </md-button>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\">\n            No entity selected\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            Attribute 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.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\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 $q;\nlet $http;\n\nself.onInit = function() {\n\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    $q = $scope.$injector.get('$q');\n    $http = $scope.$injector.get('$http');\n    settings = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.requiredErrorMessage = settings.requiredErrorMessage || \"Entity timeseries are required\";\n    $scope.labelValue = settings.labelValue || \"Value\";\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[0].type != \"timeseries\") {\n            $scope.isValidParameter = false;\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            saveEntityTimeseries(\n                datasource.entityType,\n                datasource.entityId,\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    $scope.changeFocus = function () {\n        if ($scope.currentValue === $scope.originalValue) {\n            $scope.isFocused = false;\n        }\n    }\n\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\n        var deferred = $q.defer();\n        var telemetriesData = {};\n        for (var a = 0; a < telemetries.length; a++) {\n            if (angular.isDefined(telemetries[a].value) && telemetries[a].value !== null) {\n                telemetriesData[telemetries[a].key] = telemetries[a].value;\n            }\n        }\n        if (Object.keys(telemetriesData).length) {\n            var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n            $http.post(url, telemetriesData).then(\n                function(response) {\n                    deferred.resolve(response.data);\n                },\n                function() {\n                    deferred.reject();\n                }\n            );\n        }\n        return deferred.promise;\n    }\n}\n\nself.onDataUpdated = function() {\n\n    try {\n        $scope.currentKey = self.ctx.datasources[0].dataKeys[0].label;\n        $scope.dataKeyType = self.ctx.datasources[0].dataKeys[0].type;\n\n        if (!$scope.isFocused) {\n            $scope.currentValue = $scope.originalValue = self.ctx.data[0].data[0][1];\n            correctValue($scope.currentValue);\n            $scope.$digest();\n        }\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nfunction correctValue(value) {\n    if (typeof value !== \"number\") {\n        $scope.currentValue = 0;\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        dataKeysOptional: true\n    }\n}\n\nself.onDestroy = function() {\n\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            \"showLabel\":{\n                \"title\":\"Show label\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"labelValue\": {\n                \"title\": \"Label\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"maxValue\": {\n                \"title\": \"Max value\",\n                \"type\": \"number\",\n                \"default\": \"\"\n            },\n            \"minValue\": {\n                \"title\": \"Min value\",\n                \"type\": \"number\",\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        \"showLabel\",\n        \"labelValue\",\n        \"requiredErrorMessage\",\n        \"maxValue\",\n        \"minValue\"\n    ]\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 integer timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    }
  ]
}