{
  "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        <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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\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 = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\r\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.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 === types.datasourceType.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 != types.dataKeyType.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($translate.instant('widgets.input-widgets.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($translate.instant('widgets.input-widgets.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    } 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    }\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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.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 != types.dataKeyType.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($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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    } 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    }\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 && 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                           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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n    \n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.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 != types.dataKeyType.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($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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    } 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    }\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 && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <md-checkbox ng-model=\"checkboxValue\"\n                             aria-label=\"{{ 'widgets.input-widgets.switch-attribute-value' | translate }}\"\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\">\n            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n\n    settings.trueValue = utils.customTranslation(settings.trueValue, settings.trueValue) || true;\n    settings.falseValue = utils.customTranslation(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 === types.datasourceType.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 != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.server.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: mapReverse[$scope.currentValue] || false\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\n                    }\n                }\n            );\n        }\n    };\n}\n\nself.onDataUpdated = function() {\n    try {\n        if ($scope.dataKeyDetected) {\n            $scope.checkboxValue = ($scope.originalValue = self.ctx.data[0].data[0][1]) || false;\n            $scope.currentValue = map[$scope.checkboxValue];\n            $scope.$digest();\n        }\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1\n    }\n}\n\nself.onResize = function() {}\nself.onDestroy = function() {}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"trueValue\": {\n                \"title\": \"True value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"falseValue\": {\n                \"title\": \"False value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\":{\n                \"title\":\"Show result message\",\n                \"type\":\"boolean\",\n                \"default\":true\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"showResultMessage\",\n        \"trueValue\",\n        \"falseValue\"\n    ]\n}",
        "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_server_date_attribute",
      "name": "Update server date 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      ng-class=\"{'small-width': smallWidthContainer}\"\n>\n    <div style=\"padding: 0 8px; margin: auto 0;\">\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\" ng-class=\"{'horizontal-alignment': isHorizontal && !changeAlignment}\">\n                <mdp-date-picker name=\"attributedate\" ng-model=\"currentValue\" required\n                    mdp-placeholder=\"{{ 'widgets.input-widgets.date' | translate }}\" ng-class=\"{'show-label': settings.showLabel}\">\n                    <div ng-messages=\"attrUpdateForm.attributedate.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </mdp-date-picker>\n\t            <mdp-time-picker name=\"attributetime\" ng-if=\"settings.showTimeInput\" required ng-model=\"currentValue\"\n\t                mdp-placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\" \n\t                mdp-auto-switch=\"true\" ng-class=\"{'show-label': settings.showLabel}\">\n\t                <div ng-messages=\"attrUpdateForm.attributetime.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n\t            </mdp-time-picker>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button action-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"(originalValue === currentValue) || (!currentValue)\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button action-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue;\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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-direction: column;\n    flex: 1;\n}\n.grid__element.horizontal-alignment {\n    flex-direction: row;\n}\n.grid__element:last-child {\n    align-items: center;\n    margin-left: 7px;\n}\n.grid__element {\n    display: flex;\n}\n\n.attribute-update-form .action-button {\n    margin: 0;\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.attribute-update-form mdp-date-picker,\n.attribute-update-form mdp-time-picker {\n    width: 100%;\n}\n\n.attribute-update-form mdp-date-picker md-input-container,\n.attribute-update-form mdp-time-picker md-input-container {\n    margin: 18px 0 5px;\n    width: 100%;\n}\n\n.attribute-update-form.small-width mdp-date-picker md-input-container,\n.attribute-update-form.small-width mdp-time-picker md-input-container {\n    width: 150px;\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 $translate;\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    $translate = $scope.$injector.get('$translate');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isHorizontal = (settings.inputFieldsAlignment === 'row') ? true : false;\r\n    $scope.isValidParameter = true;\r\n    $scope.entityDetected = false;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\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 === types.datasourceType.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 != types.dataKeyType.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            var currentValueInMilliseconds = $scope.currentValue.getTime();\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($translate.instant('widgets.input-widgets.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($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            $scope.currentValue = $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n            $scope.$digest();\r\n        }\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = ($scope.isHorizontal && (self.ctx.$container[0].offsetWidth < 480)) ? true : false;\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 1\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"UpdateDateAttributeSettings\",\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            \"showTimeInput\":{\n                \"title\":\"Show time input field\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"inputFieldsAlignment\": {\n                \"title\": \"Input fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"column\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\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        \"showLabel\",\n        \"showTimeInput\",\n        {\n            \"key\": \"inputFieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column (default)\"\n                },\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row\"\n                }\n            ]\n        },\n        \"requiredErrorMessage\"\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 date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_server_image_attribute",
      "name": "Update server image 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    <div style=\"padding: 0 8px; margin: auto 0;\">\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <div class=\"tb-image-select-wrapper\">\n                    <div flow-init=\"{singleFile:true}\" flow-file-added=\"imageAdded($file)\" class=\"tb-image-select-container\">\n                        <div class=\"tb-image-preview-container\" ng-if=\"settings.displayPreview\">\n                            <div ng-show=\"!currentValue\" translate>dashboard.no-image</div>\n                            <img ng-show=\"currentValue\" class=\"tb-image-preview\" src=\"{{currentValue}}\"/>\n                        </div>\n                        <div class=\"tb-image-clear-container\" ng-if=\"settings.displayClearButton\">\n                            <md-button ng-click=\"clearImage()\" \n                                class=\"tb-image-clear-btn md-icon-button md-primary\"\n                                aria-label=\"{{ 'action.remove' | translate }}\">\n                                <md-tooltip md-direction=\"top\">{{ 'action.remove' | translate }}</md-tooltip>\n                                <md-icon aria-label=\"{{ 'action.remove' | translate }}\" class=\"material-icons\">close</md-icon>\n                            </md-button>\n                        </div>\n                        <div class=\"tb-flow-drop\" flow-drop>\n                            <label for=\"select\" translate>dashboard.drop-image</label>\n                            <input class=\"file-input\" flow-btn flow-attrs=\"{accept:'image/*'}\" id=\"select\">\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\">\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\">\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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    align-items: center;\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.tb-image-select-wrapper {\n    width: 100%;\n}\n\n.tb-image-select-wrapper>label {\n    display: none;\n}\n\n.tb-image-select-wrapper>label.show-label {\n    display: block;\n}\n\n.tb-image-preview-container,\n.tb-flow-drop {\n    box-sizing: border-box;\n}\n\n.tb-image-preview {\n    max-width: 98px;\n    max-height: 98px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n    font-size: 16px;\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.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 != types.dataKeyType.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($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\n                    }\n                }\n            );\n        }\n    };\n    \n    $scope.imageAdded = function ($file) {\n        var reader = new FileReader();\n        reader.onload = function(event) {\n            $scope.$apply(function() {\n               if (event.target.result && event.target.result.startsWith('data:image/')) {\n                   $scope.attrUpdateForm.$setDirty();\n                   $scope.currentValue = event.target.result;\n               } \n            });\n        };\n        reader.readAsDataURL($file.file);\n    };\n    \n    $scope.clearImage = function () {\n        $scope.attrUpdateForm.$setDirty();\n        $scope.currentValue = null;\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                $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    }\n}\n\nself.onDestroy = function() {\n\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"UpdateImageAttributeSettings\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\":{\n                \"title\":\"Show result message\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"displayPreview\":{\n                \"title\":\"Display preview\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"displayClearButton\":{\n                \"title\":\"Display clear button\",\n                \"type\":\"boolean\",\n                \"default\":false\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"showResultMessage\",\n        \"displayPreview\",\n        \"displayClearButton\"\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 image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_server_location_attribute",
      "name": "Update server location 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    ng-class=\"{'small-width': smallWidthContainer}\"\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\" ng-class=\"{'horizontal-alignment': isHorizontal && !changeAlignment}\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\">\n                    <label>{{latLabel}}</label>\n                    <input required\n                           name=\"latitude\"\n                           ng-model=\"currentLat\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"90\"\n                           min=\"-90\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.latitude.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\">\n                    <label>{{lngLabel}}</label>\n                    <input required\n                           name=\"longitude\"\n                           ng-model=\"currentLng\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"180\"\n                           min=\"-180\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.longitude.$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 getLocation\"\n                           aria-label=\"{{ 'widgets.input-widgets.get-location' | translate }}\"\n                           ng-click=\"getCoordinate()\"\n                           ng-hide=\"!settings.showGetLocation\"\n                >\n                    <md-icon>my_location</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.get-location' | translate }}</md-tooltip>\n                </md-button>\n                \n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"discardChange()\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected && isValidParameter\">\n            {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\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-direction: column;\n    flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n    flex-direction: row;\n}\n\n.grid__element:last-child {\n    align-items: center;\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;\n    line-height: 20px;\n}\n\n.attribute-update-form .md-button.getLocation {\n    margin-right: 10px;\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.attribute-update-form md-input-container{\n    width: 100%;\n    margin: 18px 0 5px;\n}\n\n.attribute-update-form.small-width md-input-container{\n    width: 150px;\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 $translate;\r\n\r\nself.onInit = function() {\r\n    console.log(self.ctx);\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    $translate = $scope.$injector.get('$translate');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.isHorizontal = (settings.inputFieldsAlignment === 'row') ? true : false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n    $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || $translate.instant('widgets.input-widgets.latitude');\r\n    $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || $translate.instant('widgets.input-widgets.longitude');\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 === types.datasourceType.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 > 1) {\r\n            $scope.dataKeyDetected = true;\r\n            for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n                if (datasource.dataKeys[i].type != types.dataKeyType.attribute){\r\n                    $scope.isValidParameter = false;\r\n                }\r\n                if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n                    $scope.dataKeyDetected = false;\r\n                }\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                    key: settings.latKeyName,\r\n                    value: $scope.currentLat\r\n                },{\r\n                    key: settings.lngKeyName,\r\n                    value: $scope.currentLng\r\n                }]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalLat = $scope.currentLat;\r\n                    $scope.originalLng = $scope.currentLng;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000,\r\n                            angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), \r\n                            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.currentLat === $scope.originalLat || $scope.currentLng ===$scope.originalLng) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n\r\n    $scope.discardChange = function() {\r\n        $scope.currentLat = $scope.originalLat;\r\n        $scope.currentLng = $scope.originalLng;\r\n        scope.isFocused = false;\r\n    }\r\n    \r\n    $scope.disableButton = function () {\r\n        return $scope.currentLat === $scope.originalLat && $scope.currentLng === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null ;\r\n    }\r\n    \r\n    $scope.getCoordinate = function() {\r\n        if (navigator.geolocation) {\r\n            navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n                toast.showError($translate.instant('widgets.input-widgets.blocked-location'), \r\n                                    angular.element(self.ctx.$container),'bottom left');\r\n            }, {\r\n                enableHighAccuracy: settings.enableHighAccuracy\r\n            });\r\n        } else {\r\n            toast.showError($translate.instant('widgets.input-widgets.no-support-geolocation'), \r\n                            angular.element(self.ctx.$container),'bottom left');\r\n        }\r\n    }\r\n    \r\n    function showPosition(position) {\r\n        $scope.currentLat = position.coords.latitude;\r\n        $scope.currentLng = position.coords.longitude;\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            if (!$scope.isFocused) {\r\n                for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n                    if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName){\r\n                        $scope.currentLat = $scope.originalLat = self.ctx.data[i].data[0][1];\r\n                    } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName){\r\n                        $scope.currentLng = $scope.originalLng = self.ctx.data[i].data[0][1];\r\n                    }\r\n                }\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    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = ($scope.isHorizontal && (self.ctx.$container[0].offsetWidth < 480)) ? true : false;\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 2\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\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            \"latKeyName\": {\n                \"title\": \"Latitude key name\",\n                \"type\": \"string\",\n                \"default\": \"latitude\"\n            },\n            \"lngKeyName\": {\n                \"title\": \"Longitude key name\",\n                \"type\": \"string\",\n                \"default\": \"longitude\"\n            },\n            \"showLabel\": {\n                \"title\": \"Show label\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"latLabel\": {\n                \"title\": \"Label for latitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"lngLabel\": {\n                \"title\": \"Label for longitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\": {\n                \"title\": \"Show result message\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableHighAccuracy\": {\n                \"title\": \"Use high accuracy\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"showGetLocation\": {\n                \"title\": \"Show button 'Get current location'\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"inputFieldsAlignment\": {\n                \"title\": \"Input fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"column\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"latKeyName\",\n        \"lngKeyName\",\n        \"enableHighAccuracy\",\n        \"showGetLocation\",\n        \"showResultMessage\",\n        {\n            \"key\": \"inputFieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column (default)\"\n                },\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row\"\n                }\n            ]\n        },\n        \"showLabel\",\n        \"latLabel\",\n        \"lngLabel\",\n        \"requiredErrorMessage\"\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 location 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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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 && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.entity) {\n            if (datasource.entityType === types.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 = $translate.instant('widgets.input-widgets.not-allowed-entity');\n            }\n        }\n        if (datasource.dataKeys.length) {\n            if (datasource.dataKeys[0].type != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.shared.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: $scope.currentValue\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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                $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    }\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 && 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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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 && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.entity) {\n            if (datasource.entityType === types.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 = $translate.instant('widgets.input-widgets.not-allowed-entity');\n            }\n        }\n        if (datasource.dataKeys.length) {\n            if (datasource.dataKeys[0].type != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.shared.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: $scope.currentValue\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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    } 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    }\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 && 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                           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=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\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\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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 && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\n    \n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.entity) {\n            if (datasource.entityType === types.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 = $translate.instant('widgets.input-widgets.not-allowed-entity');\n            }\n        }\n        if (datasource.dataKeys.length) {\n            if (datasource.dataKeys[0].type != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.shared.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: $scope.currentValue\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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    } 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    }\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 && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <md-checkbox ng-model=\"checkboxValue\"\n                             aria-label=\"{{ 'widgets.input-widgets.switch-attribute-value' | translate }}\"\n                             ng-change=\"changed()\"\n                             ng-true-value=\"'true'\"\n                             ng-false-value=\"'false'\"\n                >\n                    {{currentValue}}\n                </md-checkbox>\n            </div>\n        </div>\n\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-hide=\"entityDetected\" ng-bind=\"message\"></div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = angular.copy(self.ctx.settings) || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\n\n    settings.trueValue = utils.customTranslation(settings.trueValue, settings.trueValue) || true;\n    settings.falseValue = utils.customTranslation(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 === types.datasourceType.entity) {\n            if (datasource.entityType === types.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 = $translate.instant('widgets.input-widgets.not-allowed-entity');\n            }\n        }\n        if (datasource.dataKeys.length) {\n            if (datasource.dataKeys[0].type != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.shared.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: mapReverse[$scope.currentValue] || false\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\n                    }\n                }\n            );\n        }\n    };\n}\n\nself.onDataUpdated = function() {\n    try {\n        $scope.checkboxValue = ($scope.originalValue = self.ctx.data[0].data[0][1]) || 'false';\n        $scope.currentValue = map[$scope.checkboxValue];\n        $scope.$digest();\n\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1\n    }\n}\n\nself.onResize = function() {}\nself.onDestroy = function() {}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"trueValue\": {\n                \"title\": \"True value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"falseValue\": {\n                \"title\": \"False value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\":{\n                \"title\":\"Show result message\",\n                \"type\":\"boolean\",\n                \"default\":true\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"showResultMessage\",\n        \"trueValue\",\n        \"falseValue\"\n    ]\n}",
        "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_shared_date_attribute",
      "name": "Update shared date 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      ng-class=\"{'small-width': smallWidthContainer}\"\n>\n    <div style=\"padding: 0 8px; margin: auto 0;\">\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\" ng-class=\"{'horizontal-alignment': isHorizontal && !changeAlignment}\">\n                <mdp-date-picker name=\"attributedate\" ng-model=\"currentValue\" required\n                    mdp-placeholder=\"{{ 'widgets.input-widgets.date' | translate }}\" ng-class=\"{'show-label': settings.showLabel}\">\n                    <div ng-messages=\"attrUpdateForm.attributedate.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </mdp-date-picker>\n\t            <mdp-time-picker name=\"attributetime\" ng-if=\"settings.showTimeInput\" required ng-model=\"currentValue\"\n\t                mdp-placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\" \n\t                mdp-auto-switch=\"true\" ng-class=\"{'show-label': settings.showLabel}\">\n\t                <div ng-messages=\"attrUpdateForm.attributetime.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n\t            </mdp-time-picker>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button action-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"(originalValue === currentValue) || (!currentValue)\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button action-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue;\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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 && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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-direction: column;\n    flex: 1;\n}\n.grid__element.horizontal-alignment {\n    flex-direction: row;\n}\n.grid__element:last-child {\n    align-items: center;\n    margin-left: 7px;\n}\n.grid__element {\n    display: flex;\n}\n\n.attribute-update-form .action-button {\n    margin: 0;\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.attribute-update-form mdp-date-picker,\n.attribute-update-form mdp-time-picker {\n    width: 100%;\n}\n\n.attribute-update-form mdp-date-picker md-input-container,\n.attribute-update-form mdp-time-picker md-input-container {\n    margin: 18px 0 5px;\n    width: 100%;\n}\n\n.attribute-update-form.small-width mdp-date-picker md-input-container,\n.attribute-update-form.small-width mdp-time-picker md-input-container {\n    width: 150px;\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 $translate;\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    $translate = $scope.$injector.get('$translate');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isHorizontal = (settings.inputFieldsAlignment === 'row') ? true : false;\r\n    $scope.isValidParameter = true;\r\n    $scope.entityDetected = false;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-attribute-required');\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 === types.datasourceType.entity) {\r\n            if (datasource.entityType === types.entityType.device) {\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            } else {\r\n                $scope.message = $translate.instant('widgets.input-widgets.not-allowed-entity');\r\n            }\r\n        }\r\n        if (datasource.dataKeys.length) {\r\n            if (datasource.dataKeys[0].type != types.dataKeyType.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            var currentValueInMilliseconds = $scope.currentValue.getTime();\r\n\r\n            attributeService.saveEntityAttributes(\r\n                datasource.entityType,\r\n                datasource.entityId,\r\n                types.attributesScope.shared.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($translate.instant('widgets.input-widgets.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($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            $scope.currentValue = $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n            $scope.$digest();\r\n        }\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = ($scope.isHorizontal && (self.ctx.$container[0].offsetWidth < 480)) ? true : false;\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 1\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"UpdateDateAttributeSettings\",\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            \"showTimeInput\":{\n                \"title\":\"Show time input field\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"inputFieldsAlignment\": {\n                \"title\": \"Input fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"column\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\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        \"showLabel\",\n        \"showTimeInput\",\n        {\n            \"key\": \"inputFieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column (default)\"\n                },\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row\"\n                }\n            ]\n        },\n        \"requiredErrorMessage\"\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 date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_shared_image_attribute",
      "name": "Update shared image 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    <div style=\"padding: 0 8px; margin: auto 0;\">\n        <div class=\"attribute-update-form__grid\" ng-show=\"entityDetected && isValidParameter && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <div class=\"tb-image-select-wrapper\">\n                    <div flow-init=\"{singleFile:true}\" flow-file-added=\"imageAdded($file)\" class=\"tb-image-select-container\">\n                        <div class=\"tb-image-preview-container\" ng-if=\"settings.displayPreview\">\n                            <div ng-show=\"!currentValue\" translate>dashboard.no-image</div>\n                            <img ng-show=\"currentValue\" class=\"tb-image-preview\" src=\"{{currentValue}}\"/>\n                        </div>\n                        <div class=\"tb-image-clear-container\" ng-if=\"settings.displayClearButton\">\n                            <md-button ng-click=\"clearImage()\" \n                                class=\"tb-image-clear-btn md-icon-button md-primary\"\n                                aria-label=\"{{ 'action.remove' | translate }}\">\n                                <md-tooltip md-direction=\"top\">{{ 'action.remove' | translate }}</md-tooltip>\n                                <md-icon aria-label=\"{{ 'action.remove' | translate }}\" class=\"material-icons\">close</md-icon>\n                            </md-button>\n                        </div>\n                        <div class=\"tb-flow-drop\" flow-drop>\n                            <label for=\"select\" translate>dashboard.drop-image</label>\n                            <input class=\"file-input\" flow-btn flow-attrs=\"{accept:'image/*'}\" id=\"select\">\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"grid__element\">\n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"isFocused = false\">\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-attribute' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"originalValue === currentValue\"\n                           ng-click=\"currentValue = originalValue; isFocused = false\">\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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 && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\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    align-items: center;\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.tb-image-select-wrapper {\n    width: 100%;\n}\n\n.tb-image-select-wrapper>label {\n    display: none;\n}\n\n.tb-image-select-wrapper>label.show-label {\n    display: block;\n}\n\n.tb-image-preview-container,\n.tb-flow-drop {\n    box-sizing: border-box;\n}\n\n.tb-image-preview {\n    max-width: 98px;\n    max-height: 98px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n    font-size: 16px;\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 $translate;\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    $translate = $scope.$injector.get('$translate');\n    settings = self.ctx.settings || {};\n    $scope.settings = settings;\n    $scope.isValidParameter = true;\n    $scope.dataKeyDetected = false;\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.entity) {\n            if (datasource.entityType === types.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 = $translate.instant('widgets.input-widgets.not-allowed-entity');\n            }\n        }\n        if (datasource.dataKeys.length) {\n            if (datasource.dataKeys[0].type != types.dataKeyType.attribute) {\n                $scope.isValidParameter = false;\n            } else {\n                $scope.currentKey = datasource.dataKeys[0].name;\n                $scope.dataKeyType = datasource.dataKeys[0].type;\n                $scope.dataKeyDetected = true;\n            }\n        }\n    }\n\n    self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n    $scope.updateAttribute = function () {\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entityType,\n                datasource.entityId,\n                types.attributesScope.shared.value,\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: $scope.currentValue\n                    }\n                ]\n            ).then(\n                function success() {\n                    $scope.originalValue = $scope.currentValue;\n                    if (settings.showResultMessage) {\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), angular.element(self.ctx.$container), 'bottom left');\n                    }\n                }\n            );\n        }\n    };\n    \n    $scope.imageAdded = function ($file) {\n        var reader = new FileReader();\n        reader.onload = function(event) {\n            $scope.$apply(function() {\n               if (event.target.result && event.target.result.startsWith('data:image/')) {\n                   $scope.attrUpdateForm.$setDirty();\n                   $scope.currentValue = event.target.result;\n               } \n            });\n        };\n        reader.readAsDataURL($file.file);\n    };\n    \n    $scope.clearImage = function () {\n        $scope.attrUpdateForm.$setDirty();\n        $scope.currentValue = null;\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                $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    }\n}\n\nself.onDestroy = function() {\n\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"UpdateImageAttributeSettings\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\":{\n                \"title\":\"Show result message\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"displayPreview\":{\n                \"title\":\"Display preview\",\n                \"type\":\"boolean\",\n                \"default\":true\n            },\n            \"displayClearButton\":{\n                \"title\":\"Display clear button\",\n                \"type\":\"boolean\",\n                \"default\":false\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"showResultMessage\",\n        \"displayPreview\",\n        \"displayClearButton\"\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 image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_shared_location_attribute",
      "name": "Update shared location 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    ng-class=\"{'small-width': smallWidthContainer}\"\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\" ng-class=\"{'horizontal-alignment': isHorizontal && !changeAlignment}\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\">\n                    <label>{{latLabel}}</label>\n                    <input required\n                           name=\"latitude\"\n                           ng-model=\"currentLat\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"90\"\n                           min=\"-90\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.latitude.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\">\n                    <label>{{lngLabel}}</label>\n                    <input required\n                           name=\"longitude\"\n                           ng-model=\"currentLng\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"180\"\n                           min=\"-180\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.longitude.$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 getLocation\"\n                           aria-label=\"{{ 'widgets.input-widgets.get-location' | translate }}\"\n                           ng-click=\"getCoordinate()\"\n                           ng-hide=\"!settings.showGetLocation\"\n                >\n                    <md-icon>my_location</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.get-location' | translate }}</md-tooltip>\n                </md-button>\n                \n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"discardChange()\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected && isValidParameter\">\n            {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\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-direction: column;\n    flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n    flex-direction: row;\n}\n\n.grid__element:last-child {\n    align-items: center;\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;\n    line-height: 20px;\n}\n\n.attribute-update-form .md-button.getLocation {\n    margin-right: 10px;\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.attribute-update-form md-input-container{\n    width: 100%;\n    margin: 18px 0 5px;\n}\n\n.attribute-update-form.small-width md-input-container{\n    width: 150px;\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 $translate;\r\n\r\nself.onInit = function() {\r\n    console.log(self.ctx);\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    $translate = $scope.$injector.get('$translate');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.isHorizontal = (settings.inputFieldsAlignment === 'row') ? true : false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n    $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || $translate.instant('widgets.input-widgets.latitude');\r\n    $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || $translate.instant('widgets.input-widgets.longitude');\r\n    $scope.message = $translate.instant('widgets.input-widgets.no-entity-selected');\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 === types.datasourceType.entity) {\r\n            if (datasource.entityType === types.entityType.device) {\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            } else {\r\n                $scope.message = $translate.instant('widgets.input-widgets.not-allowed-entity');\r\n            }\r\n        }\r\n        if (datasource.dataKeys.length > 1) {\r\n            $scope.dataKeyDetected = true;\r\n            for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n                if (datasource.dataKeys[i].type != types.dataKeyType.attribute){\r\n                    $scope.isValidParameter = false;\r\n                }\r\n                if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n                    $scope.dataKeyDetected = false;\r\n                }\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                    key: settings.latKeyName,\r\n                    value: $scope.currentLat\r\n                },{\r\n                    key: settings.lngKeyName,\r\n                    value: $scope.currentLng\r\n                }]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalLat = $scope.currentLat;\r\n                    $scope.originalLng = $scope.currentLng;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000,\r\n                            angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), \r\n                            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.currentLat === $scope.originalLat || $scope.currentLng ===$scope.originalLng) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n\r\n    $scope.discardChange = function() {\r\n        $scope.currentLat = $scope.originalLat;\r\n        $scope.currentLng = $scope.originalLng;\r\n        scope.isFocused = false;\r\n    }\r\n    \r\n    $scope.disableButton = function () {\r\n        return $scope.currentLat === $scope.originalLat && $scope.currentLng === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null ;\r\n    }\r\n    \r\n    $scope.getCoordinate = function() {\r\n        if (navigator.geolocation) {\r\n            navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n                toast.showError($translate.instant('widgets.input-widgets.blocked-location'), \r\n                                    angular.element(self.ctx.$container),'bottom left');\r\n            }, {\r\n                enableHighAccuracy: settings.enableHighAccuracy\r\n            });\r\n        } else {\r\n            toast.showError($translate.instant('widgets.input-widgets.no-support-geolocation'), \r\n                            angular.element(self.ctx.$container),'bottom left');\r\n        }\r\n    }\r\n    \r\n    function showPosition(position) {\r\n        $scope.currentLat = position.coords.latitude;\r\n        $scope.currentLng = position.coords.longitude;\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            if (!$scope.isFocused) {\r\n                for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n                    if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName){\r\n                        $scope.currentLat = $scope.originalLat = self.ctx.data[i].data[0][1];\r\n                    } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName){\r\n                        $scope.currentLng = $scope.originalLng = self.ctx.data[i].data[0][1];\r\n                    }\r\n                }\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    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = ($scope.isHorizontal && (self.ctx.$container[0].offsetWidth < 480)) ? true : false;\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 2\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\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            \"latKeyName\": {\n                \"title\": \"Latitude key name\",\n                \"type\": \"string\",\n                \"default\": \"latitude\"\n            },\n            \"lngKeyName\": {\n                \"title\": \"Longitude key name\",\n                \"type\": \"string\",\n                \"default\": \"longitude\"\n            },\n            \"showLabel\": {\n                \"title\": \"Show label\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"latLabel\": {\n                \"title\": \"Label for latitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"lngLabel\": {\n                \"title\": \"Label for longitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\": {\n                \"title\": \"Show result message\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableHighAccuracy\": {\n                \"title\": \"Use high accuracy\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"showGetLocation\": {\n                \"title\": \"Show button 'Get current location'\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"inputFieldsAlignment\": {\n                \"title\": \"Input fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"column\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"latKeyName\",\n        \"lngKeyName\",\n        \"enableHighAccuracy\",\n        \"showGetLocation\",\n        \"showResultMessage\",\n        {\n            \"key\": \"inputFieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column (default)\"\n                },\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row\"\n                }\n            ]\n        },\n        \"showLabel\",\n        \"latLabel\",\n        \"lngLabel\",\n        \"requiredErrorMessage\"\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 location 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 && 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=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\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\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\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.dataKeyDetected = false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-timeseries-required');\r\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.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 === types.datasourceType.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 != types.dataKeyType.timeseries) {\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            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($translate.instant('widgets.input-widgets.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($translate.instant('widgets.input-widgets.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        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    } 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    }\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 && dataKeyDetected\">\n            <div class=\"grid__element\">\n                <md-checkbox ng-model=\"checkboxValue\"\n                             aria-label=\"{{ 'widgets.input-widgets.switch-timeseries-value' | translate }}\"\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\">\n            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\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    $scope.dataKeyDetected = false;\n\n    settings.trueValue = utils.customTranslation(settings.trueValue, settings.trueValue) || true;\n    settings.falseValue = utils.customTranslation(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 === types.datasourceType.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 != types.dataKeyType.timeseries) {\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            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($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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.checkboxValue = mapReverse[$scope.originalValue = self.ctx.data[0].data[0][1]] || 'false';\n        $scope.currentValue = map[$scope.checkboxValue];\n        $scope.$digest();\n    } catch (e) {\n        console.log(e);\n    }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1\n    }\n}\n\nself.onDestroy = function() {\n\n}\n",
        "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 && 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                           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=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\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\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\r\n    $q = $scope.$injector.get('$q');\r\n    $http = $scope.$injector.get('$http');\r\n    settings = angular.copy(self.ctx.settings) || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-timeseries-required');\r\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.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 === types.datasourceType.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 != types.dataKeyType.timeseries) {\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            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($translate.instant('widgets.input-widgets.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($translate.instant('widgets.input-widgets.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        if ($scope.dataKeyDetected) {\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    }\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 && 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=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\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\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\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\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected\">\n            {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\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 $translate;\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    $translate = $scope.$injector.get('$translate');\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    $scope.dataKeyDetected = false;\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-timeseries-required');\n    $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || $translate.instant('widgets.input-widgets.value');\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === types.datasourceType.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 != types.dataKeyType.timeseries) {\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            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($translate.instant('widgets.input-widgets.update-successful'), 1000, angular.element(self.ctx.$container), 'bottom left');\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        toast.showError($translate.instant('widgets.input-widgets.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        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    } 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    }\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\":{}}"
      }
    },
    {
      "alias": "update_location_timeseries",
      "name": "Update location 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    ng-class=\"{'small-width': smallWidthContainer}\"\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\" ng-class=\"{'horizontal-alignment': isHorizontal && !changeAlignment}\">\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%;\">\n                    <label>{{latLabel}}</label>\n                    <input required\n                           name=\"latitude\"\n                           ng-model=\"currentLat\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"90\"\n                           min=\"-90\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.latitude.$error\">\n                        <div ng-message=\"required\">{{requiredErrorMessage}}</div>\n                    </div>\n                </md-input-container>\n                <md-input-container ng-class=\"{'show-label': settings.showLabel}\" class=\"md-block\" style=\"width: 100%\">\n                    <label>{{lngLabel}}</label>\n                    <input required\n                           name=\"longitude\"\n                           ng-model=\"currentLng\"\n                           ng-focus=\"isFocused = true\"\n                           ng-blur=\"changeFocus()\"\n                           type=\"number\"\n                           step=\"any\"\n                           max=\"180\"\n                           min=\"-180\"\n                    >\n                    <div ng-messages=\"attrUpdateForm.longitude.$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 getLocation\"\n                           aria-label=\"{{ 'widgets.input-widgets.get-location' | translate }}\"\n                           ng-click=\"getCoordinate()\"\n                           ng-hide=\"!settings.showGetLocation\"\n                >\n                    <md-icon>my_location</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.get-location' | translate }}</md-tooltip>\n                </md-button>\n                \n                <md-button class=\"md-icon-button applyChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.update-timeseries' | translate }}\"\n                           type=\"submit\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"isFocused = false\"\n                >\n                    <md-icon>check</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.update-timeseries' | translate }}</md-tooltip>\n                </md-button>\n                <md-button class=\"md-icon-button discardChanges\"\n                           aria-label=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\n                           ng-disabled=\"disableButton()\"\n                           ng-click=\"discardChange()\"\n                >\n                    <md-icon>close</md-icon>\n                    <md-tooltip md-direction=\"top\">{{ 'widgets.input-widgets.discard-changes' | translate }}</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            {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !isValidParameter\">\n            {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n        </div>\n        <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" ng-show=\"entityDetected && !dataKeyDetected && isValidParameter\">\n            {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\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-direction: column;\n    flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n    flex-direction: row;\n}\n\n.grid__element:last-child {\n    align-items: center;\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;\n    line-height: 20px;\n}\n\n.attribute-update-form .md-button.getLocation {\n    margin-right: 10px;\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.attribute-update-form md-input-container{\n    width: 100%;\n    margin: 18px 0 5px;\n}\n\n.attribute-update-form.small-width md-input-container{\n    width: 150px;\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 $translate;\r\n\r\nself.onInit = function() {\r\n    console.log(self.ctx);\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    $translate = $scope.$injector.get('$translate');\r\n    settings = self.ctx.settings || {};\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;\r\n    $scope.dataKeyDetected = false;\r\n    $scope.isHorizontal = (settings.inputFieldsAlignment === 'row') ? true : false;\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || $translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n    $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || $translate.instant('widgets.input-widgets.latitude');\r\n    $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || $translate.instant('widgets.input-widgets.longitude');\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 === types.datasourceType.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 > 1) {\r\n            $scope.dataKeyDetected = true;\r\n            for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n                if (datasource.dataKeys[i].type != types.dataKeyType.timeseries){\r\n                    $scope.isValidParameter = false;\r\n                }\r\n                if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n                    $scope.dataKeyDetected = false;\r\n                }\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.saveEntityTimeseries(\r\n                datasource.entityType,\r\n                datasource.entityId,\r\n                'scope',\r\n                [{\r\n                    key: settings.latKeyName,\r\n                    value: $scope.currentLat\r\n                },{\r\n                    key: settings.lngKeyName,\r\n                    value: $scope.currentLng\r\n                }]\r\n            ).then(\r\n                function success() {\r\n                    $scope.originalLat = $scope.currentLat;\r\n                    $scope.originalLng = $scope.currentLng;\r\n                    if (settings.showResultMessage) {\r\n                        toast.showSuccess($translate.instant('widgets.input-widgets.update-successful'), 1000,\r\n                            angular.element(self.ctx.$container), 'bottom left');\r\n                    }\r\n                },\r\n                function fail() {\r\n                    if (settings.showResultMessage) {\r\n                        toast.showError($translate.instant('widgets.input-widgets.update-failed'), \r\n                            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.currentLat === $scope.originalLat || $scope.currentLng ===$scope.originalLng) {\r\n            $scope.isFocused = false;\r\n        }\r\n    }\r\n\r\n    $scope.discardChange = function() {\r\n        $scope.currentLat = $scope.originalLat;\r\n        $scope.currentLng = $scope.originalLng;\r\n        scope.isFocused = false;\r\n    }\r\n    \r\n    $scope.disableButton = function () {\r\n        return $scope.currentLat === $scope.originalLat && $scope.currentLng === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null ;\r\n    }\r\n    \r\n    $scope.getCoordinate = function() {\r\n        if (navigator.geolocation) {\r\n            navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n                toast.showError($translate.instant('widgets.input-widgets.blocked-location'), \r\n                                    angular.element(self.ctx.$container),'bottom left');\r\n            }, {\r\n                enableHighAccuracy: settings.enableHighAccuracy\r\n            });\r\n        } else {\r\n            toast.showError($translate.instant('widgets.input-widgets.no-support-geolocation'), \r\n                            angular.element(self.ctx.$container),'bottom left');\r\n        }\r\n    }\r\n    \r\n    function showPosition(position) {\r\n        $scope.currentLat = position.coords.latitude;\r\n        $scope.currentLng = position.coords.longitude;\r\n    }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    try {\r\n        if ($scope.dataKeyDetected) {\r\n            if (!$scope.isFocused) {\r\n                for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n                    if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName){\r\n                        $scope.currentLat = $scope.originalLat = self.ctx.data[i].data[0][1];\r\n                    } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName){\r\n                        $scope.currentLng = $scope.originalLng = self.ctx.data[i].data[0][1];\r\n                    }\r\n                }\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    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = ($scope.isHorizontal && (self.ctx.$container[0].offsetWidth < 480)) ? true : false;\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1,\r\n        maxDataKeys: 2\r\n    }\r\n}\r\n\r\nself.onDestroy = function() {\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            \"latKeyName\": {\n                \"title\": \"Latitude key name\",\n                \"type\": \"string\",\n                \"default\": \"latitude\"\n            },\n            \"lngKeyName\": {\n                \"title\": \"Longitude key name\",\n                \"type\": \"string\",\n                \"default\": \"longitude\"\n            },\n            \"showLabel\": {\n                \"title\": \"Show label\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"latLabel\": {\n                \"title\": \"Label for latitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"lngLabel\": {\n                \"title\": \"Label for longitude\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showResultMessage\": {\n                \"title\": \"Show result message\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableHighAccuracy\": {\n                \"title\": \"Use high accuracy\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"showGetLocation\": {\n                \"title\": \"Show button 'Get current location'\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"inputFieldsAlignment\": {\n                \"title\": \"Input fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"column\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"latKeyName\",\n        \"lngKeyName\",\n        \"enableHighAccuracy\",\n        \"showGetLocation\",\n        \"showResultMessage\",\n        {\n            \"key\": \"inputFieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column (default)\"\n                },\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row\"\n                }\n            ]\n        },\n        \"showLabel\",\n        \"latLabel\",\n        \"lngLabel\",\n        \"requiredErrorMessage\"\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 location timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "update_multiple_attributes",
      "name": "Update Multiple Attributes",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3.5,
        "resources": [],
        "templateHtml": "<tb-multiple-input-widget \n    form-id=\"formId\"\n    ctx=\"ctx\">\n</tb-multiple-input-widget>",
        "templateCss": "",
        "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet toast;\r\nlet utils;\r\nlet types;\r\n\r\nself.onInit = function() {\r\n    var scope = self.ctx.$scope;\r\n    var id = self.ctx.$scope.$injector.get('utils').guid();\r\n    scope.formId = \"form-\"+id;\r\n    scope.ctx = self.ctx;\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    self.ctx.$scope.$broadcast('multiple-input-data-updated', self.ctx.$scope.formId);\r\n}\r\n\r\nself.onResize = function() {\r\n    self.ctx.$scope.$broadcast('multiple-input-resize', self.ctx.$scope.formId);\r\n}\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"MultipleInput\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"showActionButtons\":{\n                \"title\":\"Show action buttons\",\n                \"type\":\"boolean\",\n                \"default\": true\n            },\n            \"updateAllValues\": {\n                \"title\":\"Update all values, not only modified (only if action buttons are visible)\",\n                \"type\":\"boolean\",\n                \"default\": false\n            },\n            \"showResultMessage\":{\n                \"title\":\"Show result message\",\n                \"type\":\"boolean\",\n                \"default\": true\n            },\n            \"showGroupTitle\": {\n                \"title\":\"Show title for group of fields, related to different entities\",\n                \"type\":\"boolean\",\n                \"default\": false\n            },\n            \"groupTitle\": {\n                \"title\": \"Group title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"fieldsAlignment\": {\n                \"title\": \"Fields alignment\",\n                \"type\": \"string\",\n                \"default\": \"row\"\n            },\n            \"fieldsInRow\": {\n                \"title\": \"Number of fields in the row\",\n                \"type\": \"number\",\n                \"default\": \"2\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        \"showActionButtons\",\n        \"updateAllValues\",\n        \"showResultMessage\",\n        \"showGroupTitle\",\n        \"groupTitle\",\n        {\n            \"key\": \"fieldsAlignment\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"row\",\n                    \"label\": \"Row (default)\"\n                },\n                {\n                    \"value\": \"column\",\n                    \"label\": \"Column\"\n                }\n            ]\n        },\n        \"fieldsInRow\"\n    ]\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"dataKeyType\": {\n                \"title\": \"Datakey type\",\n                \"type\": \"string\",\n                \"default\": \"server\"\n            },\n            \"dataKeyValueType\": {\n                \"title\": \"Datakey value type\",\n                \"type\": \"string\",\n                \"default\": \"string\"\n            },\n            \"required\": {\n                \"title\": \"Value is required\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"isEditable\": {\n                \"title\": \"Ability to edit attribute\",\n                \"type\": \"string\",\n                \"default\": \"editable\"\n            },\n            \"disabledOnDataKey\": {\n                \"title\": \"Disable on false value of another datakey (specify datakey name)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"dataKeyHidden\": {\n                \"title\": \"Hide input field\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"step\": {\n                \"title\": \"Step interval between values (only for numbers)\",\n                \"type\": \"number\",\n                \"default\": \"1\"\n            },\n            \"requiredErrorMessage\": {\n                \"title\": \"'Required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"icon\": {\n                \"title\": \"Icon to show before input cell\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        {\n            \"key\": \"dataKeyType\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"server\",\n                    \"label\": \"Server attribute (default)\"\n                },\n                {\n                    \"value\": \"shared\",\n                    \"label\": \"Shared attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Timeseries\"\n                }\n            ]\n        },\n        {\n            \"key\": \"dataKeyValueType\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"string\",\n                    \"label\": \"String\"\n                },\n                {\n                    \"value\": \"double\",\n                    \"label\": \"Double\"\n                },\n                {\n                    \"value\": \"integer\",\n                    \"label\": \"Integer\"\n                },\n                {\n                    \"value\": \"booleanCheckbox\",\n                    \"label\": \"Boolean (Checkbox)\"\n                },\n                {\n                    \"value\": \"booleanSwitch\",\n                    \"label\": \"Boolean (Switch)\"\n                },\n                {\n                    \"value\": \"dateTime\",\n                    \"label\": \"Date & Time\"\n                },\n                {\n                    \"value\": \"date\",\n                    \"label\": \"Date\"\n                },\n                {\n                    \"value\": \"time\",\n                    \"label\": \"Time\"\n                }\n            ]\n        },\n        \"required\",\n        {\n            \"key\": \"isEditable\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"editable\",\n                    \"label\": \"Editable (default)\"\n                },\n                {\n                    \"value\": \"disabled\",\n                    \"label\": \"Disabled\"\n                },\n                {\n                    \"value\": \"readonly\",\n                    \"label\": \"Read-only\"\n                }\n            ]\n        },\n        \"disabledOnDataKey\",\n        \"dataKeyHidden\",\n        \"step\",\n        \"requiredErrorMessage\",\n\t\t{\n    \t\t\"key\": \"icon\",\n\t\t\t\"type\": \"icon\"\n\t\t}\n    ]\n}\n",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "web_camera_input",
      "name": "Web Camera Input",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "resources": [],
        "templateHtml": "<tb-web-camera-widget ctx=\"ctx\">\n</tb-web-camera-widget>",
        "templateCss": "",
        "controllerScript": "self.onInit = function() {\n    var scope = self.ctx.$scope;\n    scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1\n    }\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Web Camera\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"imageFormat\": {\n                \"title\": \"Image Format\",\n                \"type\": \"string\",\n                \"default\": \"image/png\"\n            },\n            \"imageQuality\":{\n                \"title\":\"Image quality that use lossy compression such as jpeg and webp\",\n                \"type\":\"number\",\n                \"default\": 0.92,\n                \"min\": 0,\n                \"max\": 1\n            },\n            \"maxWidth\": {\n                \"title\": \"The maximal image width\",\n                \"type\": \"number\",\n                \"default\": 640\n            }, \n            \"maxHeight\": {\n                \"title\": \"The maximal image heigth\",\n                \"type\": \"number\",\n                \"default\": 480\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"widgetTitle\",\n        {\n            \"key\": \"imageFormat\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"image/jpeg\",\n                    \"label\": \"JPEG\"\n                },\n                {\n                    \"value\": \"image/png\",\n                    \"label\": \"PNG\"\n                },\n                {\n                    \"value\": \"image/webp\",\n                    \"label\": \"WEBP\"\n                }\n            ]\n        },\n        \"imageQuality\",\n        \"maxWidth\",\n        \"maxHeight\"\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\":\"Web Camera Input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "device_claiming_widget",
      "name": "Device claiming widget",
      "descriptor": {
        "type": "static",
        "sizeX": 7.5,
        "sizeY": 4.5,
        "resources": [],
        "templateHtml": "<form name=\"claimDeviceForm\" class=\"claim-form\" ng-submit=\"claim()\">\n    <fieldset ng-disabled=\"$root.loading || loading\">\n        <md-input-container ng-class=\"{'show-label': showLabel}\" class=\"md-block\">\n            <label>{{deviceLabel}}</label>\n            <input ng-model=\"deviceObj.deviceName\" \n                   name=\"deviceName\" \n                   required\n            >\n            <div ng-messages=\"claimDeviceForm.deviceName.$error\">\n                <div ng-message=\"required\">{{requiredErrorDevice}}</div>\n            </div>\n        </md-input-container>\n        <md-input-container ng-if=\"secretKeyField\" class=\"md-block\" ng-class=\"{'show-label': showLabel}\">\n            <label>{{secretKeyLabel}}</label>\n            <input name=\"deviceSecret\" ng-model=\"deviceObj.deviceSecret\" required>\n            <div ng-messages=\"claimDeviceForm.deviceSecret.$error\">\n                <div ng-message=\"required\">{{requiredErrorSecretKey}}</div>\n            </div>\n        </md-input-container>\n    </fieldset>\n    <div class=\"md-block\" layout=\"row\" layout-align=\"end center\">\n        <md-button type=\"submit\" ng-disabled=\"loading || claimDeviceForm.$invalid || !claimDeviceForm.$dirty\" class=\"md-raised md-primary\">{{labelClaimButon}}</md-button>\n    </div>\n</form>",
        "templateCss": ".claim-form {\n    overflow: hidden;\n    height: 100%;\n    display: flex;\n    flex-direction: column;\n}\n\n.show-label label {\n    display: block;\n}\n\nlabel {\n    display: none;\n}",
        "controllerScript": "let $scope;\n\nself.onInit = function() {\n    $scope = self.ctx.$scope;\n    let $injector = $scope.$injector;\n    let $q = $injector.get('$q');\n    let $http = $injector.get('$http');\n    let toast = $scope.$injector.get('toast');\n    let utils = $scope.$injector.get('utils');\n    let $translate = $scope.$injector.get('$translate');\n    let $rootScope = $scope.$injector.get('$rootScope');\n    let settings = self.ctx.settings || {};\n    $scope.secretKeyField = settings.deviceSecret;\n    $scope.showLabel = settings.showLabel;\n    $scope.deviceObj = {};\n    \n    const config = {\n      ignoreErrors: true  \n    };\n    \n    let titleTemplate = \"\";\n    let successfulClaim = utils.customTranslation(settings.successfulClaimDevice, settings.successfulClaimDevice) || $translate.instant('widgets.input-widgets.claim-successful');\n    let failedClaimDevice = utils.customTranslation(settings.failedClaimDevice, settings.failedClaimDevice) || $translate.instant('widgets.input-widgets.claim-failed');\n    let deviceNotFound = utils.customTranslation(settings.deviceNotFound, settings.deviceNotFound) || $translate.instant('widgets.input-widgets.claim-not-found');\n    \n    if (settings.widgetTitle && settings.widgetTitle.length) {\n        titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n    } else {\n        titleTemplate = self.ctx.widgetConfig.title;\n    }\n    self.ctx.widgetTitle = titleTemplate;\n    \n    $scope.deviceLabel = utils.customTranslation(settings.deviceLabel, settings.deviceLabel) || $translate.instant('widgets.input-widgets.device-name');\n    $scope.requiredErrorDevice= utils.customTranslation(settings.requiredErrorDevice, settings.requiredErrorDevice) || $translate.instant('widgets.input-widgets.device-name-required');\n    \n    $scope.secretKeyLabel = utils.customTranslation(settings.secretKeyLabel, settings.secretKeyLabel) || $translate.instant('widgets.input-widgets.secret-key');\n    $scope.requiredErrorSecretKey= utils.customTranslation(settings.requiredErrorSecretKey, settings.requiredErrorSecretKey) || $translate.instant('widgets.input-widgets.secret-key-required');\n    \n    $scope.labelClaimButon = utils.customTranslation(settings.labelClaimButon, settings.labelClaimButon) || $translate.instant('widgets.input-widgets.claim-device');\n    \n    $scope.claim = () => {\n        $scope.loading = true;\n        claimDevice($scope.deviceObj.deviceName, $scope.deviceObj.deviceSecret, config).then(\n            (data) => {\n                successClaim();\n            },\n            (error) => {\n                $scope.loading = false;\n                if(error.status == 404) {\n                    toast.showError(deviceNotFound, angular.element('.claim-form'),'bottom left');\n                } else if(error.status == 400) {\n                    toast.showError(failedClaimDevice, angular.element('.claim-form'),'bottom left');\n                }\n            }\n        );\n    }\n    \n    function claimDevice(deviceName, deviceSecret, config) {\n        let deferred = $q.defer();\n        let url = \"/api/customer/device/\" + deviceName + \"/claim\";\n        let obj = deviceSecret ? { secretKey: deviceSecret } : {};\n        $http.post(url, obj, config).then(\n            (payload) => {\n                deferred.resolve(payload.data); \n            },\n            (error) => {\n                deferred.reject(error);\n            }\n        );\n        return deferred.promise;\n    }\n    \n    function updateAliasData() {\n        var aliasIds = [];\n        for (var id in self.ctx.aliasController.resolvedAliases) {\n            aliasIds.push(id);\n        }\n        var tasks = [];\n        aliasIds.forEach(function(aliasId) {\n            self.ctx.aliasController.setAliasUnresolved(aliasId);\n            tasks.push(self.ctx.aliasController.getAliasInfo(aliasId));\n        });\n        $q.all(tasks).then(function() {\n            $rootScope.$broadcast('widgetForceReInit');\n        });\n    }\n    \n    function successClaim() {\n        resetForm();\n        $scope.claimDeviceForm.$setPristine();\n        $scope.claimDeviceForm.$setUntouched();\n        $scope.loading = false;\n        toast.showSuccess(successfulClaim, 2000);\n        updateAliasData();\n    }\n    \n    function resetForm() {\n        $scope.deviceObj = {};\n    }\n}\n\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"widgetTitle\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"deviceSecret\": {\n                \"title\": \"Show 'Secret key' input field\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"showLabel\": {\n                \"title\": \"Show label\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"deviceLabel\": {\n                \"title\": \"Label for device name\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorDevice\": {\n                \"title\": \"'Device name required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"secretKeyLabel\": {\n                \"title\": \"Label for secret key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requiredErrorSecretKey\": {\n                \"title\": \"'Secret key required' error message\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"labelClaimButon\": {\n                \"title\": \"Label for claiming button\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"successfulClaimDevice\": {\n                \"title\": \"Text message of successful device claiming\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"deviceNotFound\": {\n                \"title\": \"Text message when device not found\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"failedClaimDevice\": {\n                \"title\": \"Text message of failed device claiming\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        [\n            \"widgetTitle\",\n            \"labelClaimButon\",\n            \"deviceSecret\",\n            \"showLabel\",\n            \"deviceLabel\",\n            \"secretKeyLabel\"\n        ],\n        [\n            \"deviceNotFound\",\n            \"failedClaimDevice\",\n            \"successfulClaimDevice\",\n            \"requiredErrorDevice\",\n            \"requiredErrorSecretKey\"\n        ]\n    ],\n    \"groupInfoes\": [{\n        \"formIndex\": 0,\n        \"GroupTitle\": \"General settings\"\n    }, {\n        \"formIndex\": 1,\n        \"GroupTitle\": \"Message settings\"\n    }]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"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\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"deviceSecret\":true,\"showLabel\":true},\"title\":\"Device claiming widget\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      }
    },
    {
      "alias": "markers_placement_image_map",
      "name": "Markers Placement - Image Map",
      "descriptor": {
        "type": "latest",
        "sizeX": 8.5,
        "sizeY": 6.5,
        "resources": [],
        "templateHtml": "",
        "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane         { z-index: 4; }\n\n.leaflet-tile-pane    { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane  { z-index: 5; }\n.leaflet-marker-pane  { z-index: 6; }\n.leaflet-tooltip-pane   { z-index: 7; }\n.leaflet-popup-pane   { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg    { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n    border: none;\n    background: none;\n    box-shadow: none;\n}\n\n.tb-marker-label:before {\n    border: none;\n    background: none;\n}\n",
        "controllerScript": "self.onInit = function() {\n    self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, null, null, true);\n    var createEntityLocation = {\n        name: 'action.add',\n        show: true,\n        onAction: function($event) {\n            self.ctx.map.selectEntity($event);\n        },\n        icon: 'add_location'\n    };\n    self.ctx.widgetActions = [createEntityLocation];\n}\n\nself.onDataUpdated = function() {\n    self.ctx.map.update();\n}\n\nself.onResize = function() {\n    self.ctx.map.resize();\n}\n\nself.getSettingsSchema = function() {\n    return TbMapWidgetV2.settingsSchema('image-map');\n}\n\nself.getDataKeySettingsSchema = function() {\n    return TbMapWidgetV2.dataKeySettingsSchema('image-map');\n}\n\nself.actionSources = function() {\n    return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>X Pos:</b> ${xPos:2}<br/><b>Y Pos:</b> ${yPos:2}<br/><br/><link-act name='delete'>Delete</link-act>\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapImageUrl\":\"data:image/svg+xml;base64,[encoded data, 57900 characters]\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"showTooltipAction\":\"click\",\"defaultCenterPosition\":[0,0]},\"title\":\"Markers Placement - Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"id\":\"c39f512a-21c6-6b06-3aa1-715262c6553d\",\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\\n    function(entity) {\\n        return entity.entityId === entityId.id\\n    });\\n\\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\\n    widgetContext.map.locations[0], {\\n        \\\"lat\\\": null,\\n        \\\"lng\\\": null\\n    }).then(function succes() {\\n        $rootScope.$broadcast('widgetForceReInit');\\n    });\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
      }
    },
    {
      "alias": "markers_placement_openstreetmap",
      "name": "Markers Placement - OpenStreetMap",
      "descriptor": {
        "type": "latest",
        "sizeX": 8.5,
        "sizeY": 6,
        "resources": [],
        "templateHtml": "",
        "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane         { z-index: 4; }\n\n.leaflet-tile-pane    { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane  { z-index: 5; }\n.leaflet-marker-pane  { z-index: 6; }\n.leaflet-tooltip-pane   { z-index: 7; }\n.leaflet-popup-pane   { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg    { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n    border: none;\n    background: none;\n    box-shadow: none;\n}\n\n.tb-marker-label:before {\n    border: none;\n    background: none;\n}\n",
        "controllerScript": "self.onInit = function() {\n    self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx, null, null, true);\n    var createEntityLocation = {\n        name: 'action.add',\n        show: true,\n        onAction: function($event) {\n            self.ctx.map.selectEntity($event);\n        },\n        icon: 'add_location'\n    };\n    self.ctx.widgetActions = [createEntityLocation];\n}\n\nself.onDataUpdated = function() {\n    self.ctx.map.update();\n}\n\nself.onResize = function() {\n    self.ctx.map.resize();\n}\n\nself.getSettingsSchema = function() {\n    return TbMapWidgetV2.settingsSchema('openstreet-map');\n}\n\nself.getDataKeySettingsSchema = function() {\n    return TbMapWidgetV2.dataKeySettingsSchema('openstreet-map');\n}\n\nself.actionSources = function() {\n    return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7867521952070078,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7040053227577256,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><br/><link-act name='delete'>Delete</link-act>\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":[0,0],\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"defaultZoomLevel\":5},\"title\":\"Markers Placement - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"id\":\"54c293c4-9ca6-e34f-dc6a-0271944c1c66\",\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\\n    function(entity) {\\n        return entity.entityId === entityId.id\\n    });\\n\\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\\n    widgetContext.map.locations[0], {\\n        \\\"lat\\\": null,\\n        \\\"lng\\\": null\\n    }).then(function succes() {\\n        $rootScope.$broadcast('widgetForceReInit');\\n    });\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
      }
    },
    {
      "alias": "markers_placement_google_maps",
      "name": "Markers Placement - Google Maps",
      "descriptor": {
        "type": "latest",
        "sizeX": 8.5,
        "sizeY": 6,
        "resources": [],
        "templateHtml": "",
        "templateCss": ".error {\n    color: red;\n}\n.tb-labels {\n  color: #222;\n  font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n  text-align: center;\n  width: 200px;\n  white-space: nowrap;\n}",
        "controllerScript": "self.onInit = function() {\n    self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx, null, null, true);\n    var createEntityLocation = {\n        name: 'action.add',\n        show: true,\n        onAction: function($event) {\n            self.ctx.map.selectEntity($event);\n        },\n        icon: 'add_location'\n    };\n    self.ctx.widgetActions = [createEntityLocation];\n}\n\nself.onDataUpdated = function() {\n    self.ctx.map.update();\n}\n\nself.onResize = function() {\n    self.ctx.map.resize();\n}\n\nself.getSettingsSchema = function() {\n    return TbMapWidgetV2.settingsSchema('google-map');\n}\n\nself.getDataKeySettingsSchema = function() {\n    return TbMapWidgetV2.dataKeySettingsSchema('google-map');\n}\n\nself.actionSources = function() {\n    return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n    value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><br/><link-act name='delete'>Delete</link-act>\",\"markerImageSize\":34,\"gmDefaultMapType\":\"roadmap\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"colorFunction\":\"\\n\",\"color\":\"#fe7569\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":[0,0],\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"gridSize\":60,\"defaultZoomLevel\":5},\"title\":\"Markers Placement - Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"id\":\"8d3c0156-0a14-7a6f-0ddd-0ec16b9ffc91\",\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\\n    function(entity) {\\n        return entity.entityId === entityId.id\\n    });\\n\\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\\n    widgetContext.map.locations[0], {\\n        \\\"lat\\\": null,\\n        \\\"lng\\\": null\\n    }).then(function succes() {\\n        $rootScope.$broadcast('widgetForceReInit');\\n    });\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}"
      }
    }
  ]
}
