{
  "widgetsBundle": {
    "alias": "avantec_widgets",
    "title": "Avantec widgets",
    "image": null,
    "description": null,
    "externalId": null,
    "name": "Avantec widgets"
  },
  "widgetTypes": [
    {
      "fqn": "avantec_widgets.testattributescard",
      "name": "Simple Attributes Card",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 7,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "",
        "templateCss": "#container {\r\n    overflow: auto;\r\n}\r\n\r\n.tbDatasource-container {\r\n    margin: 4px;\r\n    padding: 1px;\r\n}\r\n\r\n.tbDatasource-title {\r\n    font-size: 1.200rem;\r\n    font-weight: 500;\r\n    padding-bottom: 10px;\r\n}\r\n\r\n.tbDatasource-table {\r\n    width: 100%;\r\n    box-shadow: 0 0 10px #ccc;\r\n    border-collapse: collapse;\r\n    white-space: nowrap;\r\n    font-size: 1.000rem;\r\n    color: #757575;\r\n}\r\n\r\n.tbDatasource-table td {\r\n    position: relative;\r\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\r\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\r\n    padding: 0px 9px;\r\n    box-sizing: border-box;\r\n}",
        "controllerScript": "// Based on Cards-->Attributes Card\r\n\r\nself.onInit = function() {\r\n    \r\n    //self.ctx.datasourceTitleCells = [];\r\n    self.ctx.valueCells = [];\r\n    self.ctx.labelCells = [];\r\n    \r\n    for (var i=0; i < self.ctx.datasources.length; i++) {\r\n        var tbDatasource = self.ctx.datasources[i];\r\n\r\n        var datasourceId = 'tbDatasource' + i;\r\n        self.ctx.$container.append(\r\n            \"<div id='\" + datasourceId +\r\n            \"' class='tbDatasource-container'></div>\"\r\n        );\r\n\r\n        var datasourceContainer = $('#' + datasourceId,\r\n            self.ctx.$container);\r\n\r\n        //datasourceContainer.append(\r\n        //    \"<div class='tbDatasource-title'>\" +\r\n        //    tbDatasource.name + \"</div>\"\r\n        //);\r\n        \r\n        //var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\r\n        //self.ctx.datasourceTitleCells.push(datasourceTitleCell);\r\n        \r\n        var tableId = 'table' + i;\r\n        datasourceContainer.append(\r\n            \"<table id='\" + tableId +\r\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\r\n        );\r\n        var table = $('#' + tableId, self.ctx.$container);\r\n\r\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\r\n            var dataKey = tbDatasource.dataKeys[a];\r\n            var labelCellId = 'labelCell' + a;\r\n            var cellId = 'cell' + a;\r\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\r\n                \"</td><td id='\" + cellId +\r\n                \"'></td></tr>\");\r\n            var labelCell = $('#' + labelCellId, table);\r\n            self.ctx.labelCells.push(labelCell);\r\n            var valueCell = $('#' + cellId, table);\r\n            self.ctx.valueCells.push(valueCell);\r\n        }\r\n    }    \r\n    \r\n    self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\r\n        var cellData = self.ctx.data[i];\r\n        if (cellData && cellData.data && cellData.data.length > 0) {\r\n            var tvPair = cellData.data[cellData.data.length -\r\n                1];\r\n            var value = tvPair[1];\r\n            var textValue;\r\n            //toDo -> + IsNumber\r\n            \r\n            if (isNumber(value)) {\r\n                var decimals = self.ctx.decimals;\r\n                var units = self.ctx.units;\r\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\r\n                    decimals = cellData.dataKey.decimals;\r\n                }\r\n                if (cellData.dataKey.units) {\r\n                    units = cellData.dataKey.units;\r\n                }\r\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\r\n            } else {\r\n                txtValue = value;\r\n            }\r\n            self.ctx.valueCells[i].html(txtValue);\r\n        }\r\n    }\r\n    \r\n    function isNumber(n) {\r\n        return !isNaN(parseFloat(n)) && isFinite(n);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n    var datasourceTitleFontSize = self.ctx.height/8;\r\n    if (self.ctx.width/self.ctx.height <= 1.5) {\r\n        datasourceTitleFontSize = self.ctx.width/12;\r\n    }\r\n    datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\r\n    //for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\r\n    //    self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\r\n    //}\r\n    var valueFontSize = self.ctx.height/9;\r\n    var labelFontSize = self.ctx.height/9;\r\n    if (self.ctx.width/self.ctx.height <= 1.5) {\r\n        valueFontSize = self.ctx.width/15;\r\n        labelFontSize = self.ctx.width/15;\r\n    }\r\n    valueFontSize = Math.min(valueFontSize, 18);\r\n    labelFontSize = Math.min(labelFontSize, 18);\r\n\r\n    for (i = 0; i < self.ctx.valueCells; i++) {\r\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\r\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\r\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize*0.5 + 'px');\r\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\r\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\r\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize*0.5 + 'px');\r\n    }    \r\n}\r\n\r\nself.onDestroy = function() {\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: -1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    };\r\n}\r\n",
        "settingsSchema": "{}",
        "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;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.5242256504485792,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.5818890099680771,\"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\":\"Simple Attributes Card\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.styled_button_of_string_value",
      "name": "Styled button of string value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n            <button mat-button (click)=\"updateValue(buttonState?.on.value===currentValue)\"\r\n                [class.mat-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                [color]=\"(buttonState?.on.value===currentValue ? buttonState?.on.isPrimary : buttonState?.off.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"buttonState?.on.value===currentValue ? customOnStyle : customOffStyle\">\r\n                {{buttonState?.on.value===currentValue ? buttonState?.on.label : buttonState?.off.label}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\n    width: 100%;\n    height: 100%;\n}\n\n.tb-rpc-button .title-container {\n    font-size: 0.8em;\n    font-weight: normal;\n    white-space: nowrap;\n    opacity: 0.5;\n    margin: 0;\n}\n\n.tb-rpc-button .button-container div{\n    min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n    width: 100%;\n    margin: 0;\n}\n\n.tb-rpc-button .button-container .mat-button{\n    padding: 0 0px;\n}\n\n/*TC 20230529 for mat-mdc-raised-button*/\n.tb-rpc-button .button-container .mat-mdc-raised-button {\n    width: 100%;\n}\n\n.tb-rpc-button .error-container {\n    position: absolute;\n    top: 2%;\n    right: 0;\n    left: 0;\n    z-index: 4;\n    height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n    color: #ff3315;\n    white-space: nowrap;\n}",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.buttonState = settings.buttonState;\r\n    if (settings.buttonState.on.isPrimary === false) {\r\n            self.ctx.$scope.customOnStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.on.bgColor,\r\n                'color': self.ctx.$scope.buttonState.on.textColor,\r\n            };\r\n    }\r\n    if (settings.buttonState.off.isPrimary === false) {\r\n            self.ctx.$scope.customOffStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.off.bgColor,\r\n                'color': self.ctx.$scope.buttonState.off.textColor,\r\n            };\r\n    }\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    function parseValue(data) {\r\n        var parsed = data;\r\n        if (typeof settings.buttonState.on.value === \"string\") {\r\n            if (typeof data === \"boolean\") {\r\n                //parsed = data===true ? \"true\" : \"false\";\r\n                parsed = data===true ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            } else if (typeof data === \"number\") {\r\n                //parsed = data===1 ? \"true\" : \"false\";\r\n                parsed = data!==0 ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            }\r\n        } else if (typeof settings.buttonState.on.value === \"boolean\") {\r\n            if (typeof data === \"string\") {\r\n                parsed = data===\"true\" ? true : false;\r\n            } else if (typeof data === \"number\") {\r\n                parsed = data!==0 ? true : false;\r\n            }\r\n        }\r\n        return parsed;\r\n    }\r\n    let parseValueFunction = parseValue; //(data) => data;  // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = parseValue; //(data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": value    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(on_off) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        let value = on_off ? settings.buttonState.off.value : \r\n            settings.buttonState.on.value;\r\n        let converted = convertValueFunction(value);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                converted);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                converted);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                converted, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveMethod\": {\n                \"title\": \"Retrieve string value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrieveAttributeKey\": {\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveRPCMethod\": {\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"parseValueFunction\": {\n                \"title\": \"Parse value function, f(data), returns string\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update string value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \n            \"buttonState\":{\n                \"type\": \"object\",\n                \"title\": \"Button state\",\n                \"properties\": {\n                    \"on\": {\n                        \"type\": \"object\",\n                        \"title\": \"On state\",\n                        \"properties\": {\n                            \"value\": {\n                                \"title\": \"On state attribute value\",\n                                \"type\": \"string\",\n                                \"default\": \"true\",\n                                \"description\": \"On state attribute value.\"\n                            },\n                            \"label\": {\n                                \"title\": \"On state label\",\n                                \"type\": \"string\",\n                                \"default\": \"On state Label\"\n                            },\n                            \"isRaised\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Raised\",\n                                \"default\": true\n                            },\n                            \"isPrimary\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Primary color\",\n                                \"default\": true\n                            },\n                            \"bgColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"On state background color\",\n                                \"default\": null\n                            },\n                            \"textColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"On state text color\",\n                                \"default\": null\n                            }\n                        }, \n                        \"required\": [\n                            \"value\",\n                            \"label\"\n                        ]\n                    },\n                    \"off\": {\n                        \"type\": \"object\",\n                        \"title\": \"Off state\",\n                        \"properties\": {\n                            \"value\": {\n                                \"title\": \"Off state attribute value\",\n                                \"type\": \"string\",\n                                \"default\": \"false\",\n                                \"description\": \"Off state attribute value.\"\n                            },\n                            \"label\": {\n                                \"title\": \"Off state label\",\n                                \"type\": \"string\",\n                                \"default\": \"Off state label\"\n                            },\n                            \"isRaised\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Raised\",\n                                \"default\": true\n                            },\n                            \"isPrimary\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Primary color\",\n                                \"default\": false\n                            },\n                            \"bgColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"Off state background color\",\n                                \"default\": null\n                            },\n                            \"textColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"Off state text color\",\n                                \"default\": null\n                            }\n                        }, \n                        \"required\": [\n                            \"value\",\n                            \"label\"\n                        ]\n                    }\n                }, \n                \"required\": [\n                    \"on\",\n                    \"off\"\n                ]\n            },\n            \n            \"required\": [\n                \"retrieveMethod\",\n                \"updateMethod\",\n                \"requestTimeout\",\n                \"buttonState\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrieveMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC get value method\"\n                }\n            ]\n        },\n        \"retrieveAttributeKey\",\n        \"retrieveRPCMethod\",\n        {\n            \"key\": \"parseValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\",\n        \n        {\n            \"key\": \"buttonState\",\n            \"items\" :[\n                {\n                    \"key\": \"buttonState.on\",\n                    \"items\": [\n                        \"buttonState.on.value\",\n                        \"buttonState.on.label\",\n                        \"buttonState.on.isRaised\",\n                        \"buttonState.on.isPrimary\",\n                        {\n                            \"key\": \"buttonState.on.bgColor\",\n                            \"type\": \"color\"\n                        }, {\n                            \"key\": \"buttonState.on.textColor\",\n                            \"type\": \"color\"\n                        }\n                    ]\n                }, {\n                    \"key\": \"buttonState.off\",\n                    \"items\": [\n                        \"buttonState.off.value\",\n                        \"buttonState.off.label\",\n                        \"buttonState.off.isRaised\",\n                        \"buttonState.off.isPrimary\",\n                        {\n                            \"key\": \"buttonState.off.bgColor\",\n                            \"type\": \"color\"\n                        }, {\n                            \"key\": \"buttonState.off.textColor\",\n                            \"type\": \"color\"\n                        }\n                    ]\n                }\n            ]\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"buttonState\":{\"on\":{\"value\":\"true\",\"label\":\"On state Label\",\"isRaised\":true,\"isPrimary\":true},\"off\":{\"value\":\"false\",\"label\":\"Off state label\",\"isRaised\":true}}},\"title\":\"Styled button of string value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.segmented_switch_from_string_attribute_send_rpc",
      "name": "Segmented switch of string value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button *ngFor=\"let x of options\" (click)=\"updateValue(x)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n                [color]=\"(x.attributeValue===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"x.attributeValue===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{x?.optionLabel}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.optionStyle = settings.optionStyle;\r\n\r\n    self.ctx.$scope.customCheckedStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.checkedOption.isPrimary === false) {\r\n        self.ctx.$scope.customCheckedStyle['background-color']  = self.ctx.$scope.optionStyle.checkedOption.bgColor;\r\n        self.ctx.$scope.customCheckedStyle['color']             = self.ctx.$scope.optionStyle.checkedOption.textColor;\r\n    }\r\n\r\n    self.ctx.$scope.customUncheckedStyle = {};\r\n    self.ctx.$scope.customUncheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customUncheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === \"column\") {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.uncheckedOption.isPrimary === false) {\r\n        self.ctx.$scope.customUncheckedStyle['background-color']  = self.ctx.$scope.optionStyle.uncheckedOption.bgColor;\r\n        self.ctx.$scope.customUncheckedStyle['color']             = self.ctx.$scope.optionStyle.uncheckedOption.textColor;\r\n    }\r\n    \r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                option.attributeValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                option.attributeValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                option.attributeValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return data; /* console.log(data); */ \"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value; /* console.log(value); */\"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n\r\n            \"options\":{\r\n                \"type\": \"array\",\r\n                \"title\": \"Options\",\r\n                \"minItems\": 1,\r\n                \"maxItems\": 30,\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"optionLabel\": {\r\n                            \"title\": \"Option label\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"Option label\"\r\n                        },\r\n                        \"attributeValue\": {\r\n                            \"title\": \"Attribute value\",\r\n                            \"type\": \"string\",\r\n                            \"description\": \"Attribute value.\"\r\n                        }\r\n                    }, \r\n                    \"required\": [\r\n                        \"optionLabel\",\r\n                        \"attributeValue\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"retrieveMethod\",\r\n                \"updateMethod\", \r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options[]\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Segmented switch of string value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.rtc_button_with_method_params",
      "name": "RTC button with params & response",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7,
        "sizeY": 4,
        "resources": [],
        "templateHtml": "    <form #rpcForm=\"ngForm\" (submit)=\"sendCommand()\">\r\n      <div class=\"mat-content mat-padding\" fxLayout=\"column\">\r\n        <mat-form-field [fxShow]=\"requireParam\" class=\"mat-block\">\r\n          <mat-label>{{ paramLabel }}</mat-label>  \r\n          <input matInput required name=\"rpcParams\" #rpcParamsField=\"ngModel\" [(ngModel)]=\"rpcParams\"/>\r\n          <mat-error *ngIf=\"rpcParamsField.hasError('required')\">\r\n            RPC params is required.\r\n          </mat-error>\r\n        </mat-form-field>\r\n        <button [disabled]=\"requireParam && (rpcForm.invalid || !rpcForm.dirty)\" mat-raised-button color=\"primary\"  type=\"submit\" >\r\n            {{ buttonLabel }}\r\n        </button>\r\n        <div style=\"margin-top: 18px;\">\r\n          <label>RPC command response</label>\r\n          <div style=\"width: 100%; height: 60px; border: solid 2px gray\" [innerHTML]=\"rpcCommandResponse\">\r\n          </div>\r\n        </div>\r\n      </div>\r\n    </form>\r\n",
        "templateCss": "\n",
        "controllerScript": "self.onInit = function() {\r\n    let utils    = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.requireParam  = settings.requireParam;\r\n    self.ctx.$scope.paramLabel  = settings.paramLabel;\r\n    self.ctx.$scope.buttonLabel = settings.buttonLabel;\r\n    \r\n    self.ctx.$scope.sendCommand = function() {\r\n        //var rpcMethod = self.ctx.$scope.rpcMethod;\r\n        var rpcMethod = self.ctx.settings.rpcMethod;\r\n        var rpcParams = self.ctx.$scope.rpcParams;\r\n        var timeout = self.ctx.settings.requestTimeout;\r\n        var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ? true : false;\r\n        \r\n        //console.log(\"sendCommand()\");\r\n        //console.log(rpcMethod);\r\n        //console.log(rpcParams);\r\n        //console.log(timeout);\r\n        //console.log(oneWayElseTwoWay);\r\n        \r\n        var commandObservable;\r\n        if (oneWayElseTwoWay) {\r\n            commandObservable = self.ctx.controlApi.sendOneWayCommand(rpcMethod, rpcParams, timeout);\r\n        } else {\r\n            commandObservable = self.ctx.controlApi.sendTwoWayCommand(rpcMethod, rpcParams, timeout);\r\n        }\r\n        commandObservable.subscribe(\r\n            function (response) {\r\n                if (oneWayElseTwoWay) {\r\n                    self.ctx.$scope.rpcCommandResponse = \"Command was successfully received by device.<br> No response body because of one way command mode.\";\r\n                } else {\r\n                    self.ctx.$scope.rpcCommandResponse = \"Response from device:<br>\";                    \r\n                    self.ctx.$scope.rpcCommandResponse += JSON.stringify(response, undefined, 2);\r\n                }\r\n                self.ctx.detectChanges();\r\n            },\r\n            function (rejection) {\r\n                self.ctx.$scope.rpcCommandResponse = \"Failed to send command to the device:<br>\"\r\n                self.ctx.$scope.rpcCommandResponse += \"Status: \" + rejection.status + \"<br>\";\r\n                self.ctx.$scope.rpcCommandResponse += \"Status text: '\" + rejection.statusText + \"'\";\r\n                self.ctx.detectChanges();\r\n            }\r\n            \r\n        );\r\n    }\r\n    \r\n}\r\n\r\n\r\n/* \r\n        <mat-form-field class=\"mat-block\">\r\n          <mat-label>RPC method</mat-label>\r\n          <input matInput required name=\"rpcMethod\" #rpcMethodField=\"ngModel\" [(ngModel)]=\"rpcMethod\"/>\r\n          <mat-error *ngIf=\"rpcMethodField.hasError('required')\">\r\n            RPC method name is required.\r\n          </mat-error>\r\n        </mat-form-field>\r\n*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"rpcMethod\": {\r\n                \"title\": \"RPC method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpcMethod\"\r\n            },\r\n            \"oneWayElseTwoWay\": {\r\n                \"title\": \"Is One Way Command\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"requireParam\": {\r\n                \"title\": \"Is RPC param required\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"paramLabel\": {\r\n                \"title\": \"RPC param label\",\r\n                \"type\": \"string\",\r\n                \"default\": \"RPC param label\"\r\n            },\r\n            \"buttonLabel\": {\r\n                \"title\": \"Button label\",\r\n                \"type\": \"string\",\r\n                \"default\": \"Send RPC Command\"\r\n            }\r\n        },\r\n        \"required\": [\"rpcMethod\",\r\n            \"paramLabel\",\r\n            \"buttonLabel\"\r\n            ]\r\n    },\r\n    \"form\": [\r\n        \"rpcMethod\",\r\n        \"oneWayElseTwoWay\",\r\n        \"requestTimeout\",\r\n        \"requireParam\",\r\n        \"paramLabel\",\r\n        \"buttonLabel\"\r\n        ]\r\n} ",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"rpcMethod\":\"rpcMethod\",\"oneWayElseTwoWay\":true,\"paramLabel\":\"RPC param label\",\"buttonLabel\":\"Send RPC Command\",\"requireParam\":true},\"title\":\"RTC button with params & response\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_shared_integer_attribute_with_segmented_switch_",
      "name": "Update shared string attribute with segmented switch ",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{titleTemplate}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button *ngFor=\"let x of options\" (click)=\"updateAttribute(x)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n                [color]=\"(x.attributeValue===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"x.attributeValue===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{x?.optionLabel}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n/* TC 20230525 */\r\n.tb-rpc-button .button-container .mat-mdc-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n    self.ctx.ngZone.run(function() {\n       init(); \n       self.ctx.detectChanges(true);\n    });\n};\n\n\nfunction init() {\n\n    $scope = self.ctx.$scope;\n    attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n    utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n    translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n    $scope.toastTargetId = 'input-widget' + utils.guid(); // TODO: comment it?\n    settings = utils.deepClone(self.ctx.settings) || {};\n    ////settings.showLabel = utils.defaultValue(settings.showLabel, true);\n    settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n    ////settings.isRequired = utils.defaultValue(settings.isRequired, true);\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    //// $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 (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    $scope.showTitle = settings.widgetTitle && settings.widgetTitle.length ? true : false;\n    $scope.options = settings.options;\n    $scope.optionStyle = settings.optionStyle;\n\n    $scope.customCheckedStyle = {};\n    $scope.customCheckedStyle['min-width'] = '40px';\n    $scope.customCheckedStyle['padding']   = '0 2px';\n    if (settings.optionStyle.orientation === 'column') {\n        $scope.customCheckedStyle['width']     = '100%';\n        $scope.customCheckedStyle['margin']    = 0;\n    } else {\n        $scope.customCheckedStyle['width']     = 100/settings.options.length + '%';\n        $scope.customCheckedStyle['margin']    = 0;\n    }\n    if (settings.optionStyle.checkedOption.isPrimary === false) {\n        $scope.customCheckedStyle['background-color']  = $scope.optionStyle.checkedOption.bgColor;\n        $scope.customCheckedStyle['color']             = $scope.optionStyle.checkedOption.textColor;\n    }\n\n    $scope.customUncheckedStyle = {};\n    $scope.customUncheckedStyle['min-width'] = '40px';\n    $scope.customUncheckedStyle['padding']   = '0 2px';\n    if (settings.optionStyle.orientation === \"column\") {\n        $scope.customUncheckedStyle['width']     = '100%';\n        $scope.customUncheckedStyle['margin']    = 0;\n    } else {\n        $scope.customUncheckedStyle['width']     = 100/settings.options.length + '%';\n        $scope.customUncheckedStyle['margin']    = 0;\n    }\n    if (settings.optionStyle.uncheckedOption.isPrimary === false) {\n        $scope.customUncheckedStyle['background-color']  = $scope.optionStyle.uncheckedOption.bgColor;\n        $scope.customUncheckedStyle['color']             = $scope.optionStyle.uncheckedOption.textColor;\n    }\n    \n    // var validators = [\n    //     $scope.validators.min(settings.minValue),\n    //     $scope.validators.max(settings.maxValue),\n    //     $scope.validators.pattern(/^-?[0-9]+$/)\n    // ];\n    \n    // if (settings.isRequired) {\n    //     validators.push($scope.validators.required);\n    // }\n\n    // $scope.attributeUpdateFormGroup = $scope.fb.group({\n    //     currentValue: [undefined, validators]\n    // });\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === 'entity') {\n            if (datasource.entityType === 'DEVICE') {\n                if (datasource.entityType && datasource.entityId) {\n                    $scope.entityName = datasource.entityName;\n                    $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 !== \"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    // update value at click a button\n    $scope.updateAttribute = function(option) {\n        $scope.isFocused = false;\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entity.id,\n                'SHARED_SCOPE',\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: option.attributeValue //$scope.attributeUpdateFormGroup.get('currentValue').value\n                    }\n                ]\n            ).subscribe(\n                function success() {\n                    $scope.originalValue = $scope.currentValue; //$scope.attributeUpdateFormGroup.get('currentValue').value;\n                    if (settings.showResultMessage) {\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n                    }\n                }\n            );\n        }\n    };\n\n    // $scope.changeFocus = function () {\n    //     if ($scope.currentValue === $scope.originalValue) {  // $scope.attributeUpdateFormGroup.get('currentValue').value\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.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n                self.ctx.detectChanges();\n            }\n        }\n    } catch (e) {\n        console.log(e);\n    }\n}\n\n// function correctValue(value) {\n//     if (typeof value !== \"string\") { // \"number\"\n//         return 0;\n//     }\n//     return value;\n// }\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        singleEntity: true\n    }\n}\n\nself.onDestroy = function() {\n\n}",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"widgetTitle\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n\r\n            \"options\":{\r\n                \"type\": \"array\",\r\n                \"title\": \"Options\",\r\n                \"minItems\": 1,\r\n                \"maxItems\": 30,\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"optionLabel\": {\r\n                            \"title\": \"Option label\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"Option label\"\r\n                        },\r\n                        \"attributeValue\": {\r\n                            \"title\": \"Attribute value\",\r\n                            \"type\": \"string\",\r\n                            \"description\": \"Attribute value.\"\r\n                        }\r\n                    }, \r\n                    \"required\": [\r\n                        \"optionLabel\",\r\n                        \"attributeValue\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"widgetTitle\",\r\n        \"showResultMessage\",\r\n\r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options[]\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}\r\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\":\"#FFFFFF\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false,\"bgColor\":\"#e6e7e8\"}},\"options\":[{\"optionLabel\":\"Option label 0\",\"attributeValue\":\"Option label 0\"},{\"optionLabel\":\"Option label 1\",\"attributeValue\":\"Option label 1\"}],\"showResultMessage\":true},\"title\":\"Update shared string attribute with segmented switch \",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.html_attributes_cards",
      "name": "Entities Cards",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 9,
        "sizeY": 4,
        "resources": [],
        "templateHtml": "",
        "templateCss": ".card-container {\n    width: 100%;\n    /*height: fit-content;*/ /*height: 100%;*/\n    \n    padding-left: inherit;\n    padding-right: inherit;\n    \n    /*box-sizing: border-box;*/\n    /*border: #696969 1px solid;*/\n    \n\t/* 要创建一个 flex 容器，只需要将一个 display: flex 属性添加到一个元素上。\n\t默认情况下，所有的直接子元素都被认为是 flex 项，并从左到右依次排列在一行中。\n\t如果 flex 项的宽度总和大于容器，那么 flex 项将按比例缩小，直到它们适应 flex 容器宽度 */\n\tdisplay: flex;\n\t/* flex-direction 决定主轴的方向 row(默认)|row-reverse|column|column-reverse*/\n\tflex-direction: row;\n\t/* flex-wrap决定当排列不下时是否换行以及换行的方式,nowrap(默认)|wrap|wrap-reverse */\n\tflex-wrap:wrap;\n\t/* flex-flow是lex-direction和flex-wrap的简写形式，如：row wrap|column wrap-reverse等。默认值为row nowrap，即横向排列 不换行 */\n\tflex-flow:row wrap;\n\t/* !当主轴沿水平方向时!justify-content,决定item在主轴上的对齐方式，可能的值有flex-start（默认），flex-end，center，space-between，space-around */\n\tjustify-content: space-around;\n\t/* !主轴水平时!决定了item在交叉轴上的对齐方式，可能的值有flex-start|flex-end|center|baseline|stretch */\n\talign-items: flex-start;\n}\n\n.card-container .card {\n    margin: .3em;\n    \n    padding: 5px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    /*height: 100%;*/\n    box-sizing: border-box;\n    transition: background-color 0.5s;\n    \n    width: 160px;\n    height: 10em;\n\n    background-color: #f1f1f1;\n    /*box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);*/\n    box-shadow: 0px 10px 20px -13px rgba(16, 16, 16, 0.35);\n    border-radius: 3px;\n\n    text-align: center;\n\n    /*background: #2db36a;\n    color: #ffffff;*/\n}\n\n/* Style the counter cards */\n/*.card-container .card {\n  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);\n  padding: 16px;\n  text-align: center;\n  background-color: #f1f1f1;\n}*/\n\n.card-container .card .card-title {\n    /*color: #ffffff;\n    padding-bottom: 1em;\n    text-align: center;\n    opacity: 0.9;*/\n    \n    /*font-size: 1.200rem;\n    font-weight: 500;*/\n    font-size: 1.2em;\n    opacity: 0.9;\n    padding-bottom: 5px;\n}\n\n/*.card-container .card .card-line{\n    text-align: center;\n}*/\n\n.card-container .card .card-data-label{\n    /*color: #ffffff;*/\n    font-size: 0.8em;\n    opacity: 0.7;\n    /*text-align: center;*/\n}\n\n.card-container .card .card-data-pure-value {\n    /*color: #ffffff;*/\n    font-size: 1.6em;\n    font-weight: 500;\n}\n\n.card-container .card .card-data-unit {\n    /*color: #ffffff;*/\n    font-size: .6em;\n}\n\n#container {\n    overflow: auto;\n}\n\n\n.tbDatasource-container {\n    margin: 5px;\n    padding: 8px;\n}\n\n.tbDatasource-title {\n    font-size: 1.200rem;\n    font-weight: 500;\n    padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n    width: 100%;\n    box-shadow: 0 0 10px #ccc;\n    border-collapse: collapse;\n    white-space: nowrap;\n    font-size: 1.000rem;\n    color: #757575;\n}\n\n.tbDatasource-table td {\n    position: relative;\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n    padding: 0px 18px;\n    box-sizing: border-box;\n}",
        "controllerScript": "//Avantec Manufacture Limited, V1.0.0, 20230705\nself.onInit = function() {\n    var global_cell_id = 111;\n\n    self.ctx.$container.append(\n        \"<div class='card-container'></div>\"\n    );\n    var cardContainer = $('.card-container', self.ctx.$container);\n\n    /* self.ctx.datasourceTitleCells = []; */\n    /* self.ctx.valueCells = []; \n    self.ctx.labelCells = []; */\n\n    self.ctx.cells = [];\n\n    let utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n    let startIndexOfData = 0;\n    for (var i=0; i < self.ctx.datasources.length; i++) {\n\n        // append a card per datasource\n        var tbDatasource = self.ctx.datasources[i];\n        var datasourceId = 'tbDatasource' + i;\n        // self.ctx.$container.append(\n        //     \"<div id='\" + datasourceId +\n        //     \"' class='tbDatasource-container'></div>\"\n        // );\n        cardContainer.append(\n            \"<div id='\" + datasourceId +\n            \"' class='card' class='mat-primary' data-data-source-alias-name='\"\n            + tbDatasource.aliasName +\n            \"' data-id='\" + tbDatasource.entityId +\n            \"' data-entity-type='\" + tbDatasource.entityType + \n            \"' data-entity-name='\" + tbDatasource.entityName + \n            \"' data-entity-label='\" + tbDatasource.entityLabel + \n            \"'></div>\"\n        );\n        var datasourceContainer = $('#' + datasourceId,\n            self.ctx.$container);\n        datasourceContainer.click((event) => {\n            self.onCardClick(event);\n        });\n         \n        // append a title on the top of a card\n        /* datasourceContainer.append(\n            \"<div class='tbDatasource-title'>\" +\n            tbDatasource.name + \"</div>\"\n        );\n        var datasourceTitleCell = $('.tbDatasource-title',\n            datasourceContainer);\n        self.ctx.datasourceTitleCells.push(datasourceTitleCell); */\n\n        /*\n        // append a table in a card\n        var tableId = 'table' + i;\n        datasourceContainer.append(\n            \"<table id='\" + tableId +\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n        );\n        var table = $('#' + tableId, self.ctx.$container);\n        // append some tr/td for all data keys\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n            var dataKey = tbDatasource.dataKeys[a];\n            var labelCellId = 'labelCell' + a;\n            var cellId = 'cell' + a;\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n                \"</td><td id='\" + cellId +\n                \"'></td></tr>\");\n            var labelCell = $('#' + labelCellId, table);\n            self.ctx.labelCells.push(labelCell);\n            var valueCell = $('#' + cellId, table);\n            self.ctx.valueCells.push(valueCell);\n        } */\n\n        // append some cells from datasource\n        const aliasName = tbDatasource.aliasName;\n        if (self.ctx.settings.cardTemplates) {\n            // look up a template for a aliasName\n            let template = self.ctx.settings.cardTemplates.find(\n                    template => template.aliasName === aliasName);\n            //console.log(\"template=\", template);\n            if (template) {\n                let pattern = utils.createLabelFromDatasource(\n                    tbDatasource, template.cardHtmlPattern);\n                //console.log(\"pattern=\", pattern);\n                if (pattern && self.ctx.datasources && self.ctx.datasources.length) {\n                    createCellsFromPattern(datasourceContainer, self.ctx.cells, \n                        pattern, tbDatasource, startIndexOfData);\n                }\n                \n                try {\n                    //let styleFunction = (datasource, ctx) => {return {};};\n                    if (template.cardStyleFunction && template.cardStyleFunction.length) {\n                        let styleFunction = new Function(\n                            'datasource', 'ctx', template.cardStyleFunction);\n                        let css = styleFunction(tbDatasource, self.ctx);\n                        if (css) {\n                            datasourceContainer.css(css);\n                            //datasourceContainer.css('background', '#2db36a');\n                        }\n                    }\n                } catch (ex) {\n                    console.log(ex);\n                }\n            }\n        }\n        \n        startIndexOfData += tbDatasource.dataKeys.length;\n    }    \n    \n    //console.log(\"self.ctx\", self.ctx);\n    //console.log(\"self.ctx.settings\", self.ctx.settings);\n    //console.log(\"self.ctx.datasources\", self.ctx.datasources);\n    /* console.log(\"self.ctx.datasourceTitleCells[0]\", self.ctx.datasourceTitleCells[0]); */\n    \n    self.onResize();\n\n    function createCellsFromPattern(container, cells, pattern, datasource,\n            startIndexOfData) {\n        const replaceInfo = processDataPattern(pattern, datasource);\n        //console.log(\"replaceInfo=\", replaceInfo);\n        fillDataPattern(container, cells, pattern, replaceInfo, datasource,\n            startIndexOfData);\n    }\n    function fillDataPattern(container, cells, pattern, replaceInfo, datasource,\n            startIndexOfData) {\n        let ids = [];\n        let text = pattern; //utils.createLabelFromDatasource(datasource, pattern);\n        if (replaceInfo) {\n            for (const variableInfo of replaceInfo) {\n                // let txtVal = '';\n                // if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName]))\n                // {\n                //     const varData = data[variableInfo.dataKeyName];\n                //     if (isNumber(varData)) {\n                //         txtVal = padValue(varData, variableInfo.valDec);\n                //     } else {\n                //         txtVal = varData;\n                //     }\n                // }\n                // text = text.replace(variableInfo.variable, txtVal);\n                \n                let index = getDataKeyIndexFromDataSource(\n                                variableInfo.dataKeyName, datasource);\n                if (isDefinedAndNotNull(index)) {\n                    index += startIndexOfData;\n                    let cellId = \"cell_id_\"+global_cell_id; global_cell_id +=1;\n                    let txtVal =   \"<span id='\" + cellId + \"' class='card-data-pure-value'></span>\";\n                    text = text.replace(variableInfo.variable, txtVal);\n                    ids.push({cellId:cellId, index:index});\n                    //console.log(cellId, index);\n                }\n            }\n        }\n        container.append(text);\n        ids.forEach(item => {\n            var valueCell = $('#' + item.cellId, container);\n            cells.push({index:item.index, valueCell:valueCell});\n        });\n        \n        //console.log(\"ids=\", ids);\n    }\n    function processDataPattern(pattern, datasource) {\n        const replaceInfo = [];\n        try {\n            const reg = /\\${([^}]*)}/g;\n            let match = reg.exec(pattern);\n            while (match !== null) {\n                const variableInfo = {\n                    dataKeyName: '',\n                    valDec: 2,\n                    variable: ''\n                };\n                const variable = match[0];\n                let label = match[1];\n                let valDec = 2;\n                const splitValues = label.split(':');\n                if (splitValues.length > 1) {\n                    label = splitValues[0];\n                    valDec = parseFloat(splitValues[1]);\n                }\n        \n                variableInfo.variable = variable;\n                variableInfo.valDec = valDec;\n        \n                if (label.startsWith('#')) {\n                    const keyIndexStr = label.substring(1);\n                    const n = Math.floor(Number(keyIndexStr));\n                    if (String(n) === keyIndexStr && n >= 0) {\n                        variableInfo.dataKeyName = datasource.dataKeys[n].label;\n                    }\n                } else {\n                    variableInfo.dataKeyName = label;\n                }\n                replaceInfo.push(variableInfo);\n\n                match = reg.exec(pattern);\n            }\n        } catch (ex) {\n            console.log(ex, pattern);\n        }\n        return replaceInfo;\n    }\n    function getDataKeyIndexFromDataSource(datakeyLabel, datasource) {\n        let index;\n        try {\n            index  = datasource.dataKeys.findIndex(\n                (dataKey) => dataKey.label === datakeyLabel);\n            //console.log(\"datakeyLabel=\", datakeyLabel, index);\n        } catch (ex) {\n            console.log(ex);\n        }\n        return index;\n    }\n    function isDefinedAndNotNull(value) {\n        return typeof value !== 'undefined' && value !== null;\n    }\n};\n\nself.onCardClick= function(event) { //Element\n    //console.log(\"event=\", event); //alert(\"1\");\n\n    const element = event.currentTarget; // event.target || event.srcElement;\n    if (event && element.dataset && element.dataset.id) {\n        const dataSourceAliasName = element.dataset.dataSourceAliasName || '';\n        const id_ = element.dataset.id ? element.dataset.id : null\n            , entityType_ = element.dataset.entityType ? element.dataset.entityType : null\n            , entityId = {entityType: entityType_, id: id_}\n            , entityName = element.dataset.entityName ? element.dataset.entityName : null\n            , entityLabel = element.dataset.entityLabel ? element.dataset.entityLabel : null;\n        // const entityInfo = self.ctx.actionsApi.getActiveEntityInfo()\n        //     , entityId = entityInfo ? entityInfo.entityId : null\n        //     , entityName = entityInfo ? entityInfo.entityName : null\n        //     , entityLabel = entityInfo && entityInfo.entityLabel ? entityInfo.entityLabel : null;\n\n        // self.ctx.actionsApi.getActionDescriptors(actionSourceId);\n        let actionDescriptors = self.ctx.actionsApi.getActionDescriptors(\"elementClick\");\n    \n        //console.log(\"self.ctx=\", self.ctx);\n        //console.log(\"dataSourceAliasName=\", dataSourceAliasName);\n        //console.log(\"actionDescriptors=\", actionDescriptors);\n    \n        //console.log(\"entityInfo=\", entityInfo);\n        //console.log(\"entityId=\", entityId);\n        //console.log(\"entityName=\", entityName);\n        //console.log(\"entityLabel=\", entityLabel);\n    \n        if (actionDescriptors.length) {\n            const descriptor = actionDescriptors.find(actionDescriptor => actionDescriptor.name === dataSourceAliasName);\n            //console.log(\"descriptor=\", descriptor);\n            if (descriptor) {\n                event.stopPropagation();\n                // self.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName);\n                self.ctx.actionsApi.handleWidgetAction(event, descriptor, entityId, entityName, null, entityLabel);\n            }\n        }\n    } else {\n        \n    }\n};\n\nself.onDataUpdated = function() {\n    /*\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\n        var cellData = self.ctx.data[i];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length - 1];\n            var value = tvPair[1];\n            var txtValue;\n            //toDo -> + IsNumber\n            \n            if (isNumber(value)) {\n                var decimals = self.ctx.decimals;\n                var units = self.ctx.units;\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n                    decimals = cellData.dataKey.decimals;\n                }\n                if (cellData.dataKey.units) {\n                    units = cellData.dataKey.units;\n                }\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n            } else {\n                txtValue = value;\n            }\n            self.ctx.valueCells[i].html(txtValue);\n        }\n    } */\n    \n    // new TC\n    //console.log(\"self.ctx.cells=\", self.ctx.cells);\n    for (var i = 0; i < self.ctx.cells.length; i++) {\n        var cellData = self.ctx.data[self.ctx.cells[i].index];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length - 1];\n            var value = tvPair[1];\n            //toDo -> + IsNumber\n\n            // update HTML\n            var txtValue;\n            if (cellData.dataKey.settings.useCellContentFunction) {\n                txtValue = getCellContentFromFunction(value,\n                    cellData.datasource, self.ctx,\n                    cellData.dataKey.settings.cellContentFunction);\n            } else {\n                txtValue = getCellContent(value, cellData.dataKey.decimals,\n                    cellData.dataKey.units, self.ctx);\n            }\n            self.ctx.cells[i].valueCell.html(txtValue);\n            \n            // update CSS\n            if (cellData.dataKey.settings.useCellStyleFunction) {\n                //console.log(\"cellData.dataKey.settings.useCellStyleFunction=\",\n                //  cellData.dataKey.settings.useCellStyleFunction);\n                let txtStyle = getCellStyleFromFunction(value,\n                        cellData.datasource, self.ctx,\n                        cellData.dataKey.settings.cellStyleFunction);\n                self.ctx.cells[i].valueCell.css(txtStyle);\n                //console.log(\"txtStyle=\", txtStyle);\n            }\n        }\n    }\n    \n    function getCellContent(value, decimals_, units_, ctx) {\n        let txtValue = \"\";\n        if (isNumber(value)) {\n            let decimals = ctx.decimals;\n            let units = ctx.units;\n            if (decimals_ || decimals_ === 0) {\n                decimals = decimals_;\n            }\n            if (units_) {\n                units = units_;\n            }\n            txtValue = ctx.utils.formatValue(value, decimals, units, true);\n        } else {\n            txtValue = value;\n            try {\n                if (typeof value === 'undefined' || value === null || typeof value === \"string\" && value.length==0) {\n                  txtValue = \"&nbsp;\";\n                }\n            } catch (ex) {\n                console.log(ex);\n            }\n            //console.log(\"value=\", value, \"value.length=\", value.length, \"txtValue=\", txtValue);\n        }        \n        return txtValue;\n    }\n    function getCellContentFromFunction(value, tbDatasource, ctx, cellContentFunction) {\n        let txtValue = \"\";\n        try {\n            if (cellContentFunction && cellContentFunction.length) {\n                let contentFunction = new Function('value',\n                    'datasource', 'ctx', cellContentFunction);\n                txtValue = contentFunction(value, tbDatasource, ctx);\n            }\n        } catch (ex) {\n            console.log(ex);\n        }\n        return txtValue;\n    }\n    function getCellStyleFromFunction(value, tbDatasource, ctx, cellStyleFunction) {\n        let txtStyle = {};\n        try {\n            if (cellStyleFunction && cellStyleFunction.length) {\n                let styleFunction = new Function('value',\n                    'datasource', 'ctx', cellStyleFunction);\n                txtStyle = styleFunction(value, tbDatasource, ctx);\n            }\n        } catch (ex) {\n            console.log(ex);\n        }\n        return txtStyle;\n    }\n    \n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n};\n\nself.onResize = function() {\n    /* var datasourceTitleFontSize = self.ctx.height/8;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        datasourceTitleFontSize = self.ctx.width/12;\n    }\n    datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n    for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n        self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n    } */\n    var valueFontSize = self.ctx.height/9;\n    var labelFontSize = self.ctx.height/9;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        valueFontSize = self.ctx.width/15;\n        labelFontSize = self.ctx.width/15;\n    }\n    valueFontSize = Math.min(valueFontSize, 18);\n    labelFontSize = Math.min(labelFontSize, 18);\n\n    /*\n    for (i = 0; i < self.ctx.valueCells; i++) {\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n    }*/\n};\n\n// self.typeParameters = function() {\n//     // TODO:\n// };\n\nself.actionSources = function() {\n    return {\n        'elementClick': {\n            name: 'widget-action.element-click',\n            multiple: true\n        }\n    };\n};\n\nself.onDestroy = function() {\n};\n\n    // function isNumber(value) {\n    //     return typeof value === 'number';\n    // }\n    // function padValue(val, dec) {\n    //     let strVal;\n    //     let n;\n        \n    //     val = parseFloat(val);\n    //     n = (val < 0);\n    //     val = Math.abs(val);\n        \n    //     if (dec > 0) {\n    //         strVal = val.toFixed(dec);\n    //     } else {\n    //         strVal = Math.round(val).toString();\n    //     }\n    //     strVal = (n ? '-' : '') + strVal;\n    //     return strVal;\n    // }\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"cardTemplates\": {\r\n                \"type\": \"array\",\r\n                \"title\": \"Card templates\",\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"aliasName\": {\r\n                            \"type\": \"string\",\r\n                            \"title\": \"Alias Name\",\r\n                            \"minLength\": 1\r\n                        },\r\n                        \"cardHtmlPattern\": {\r\n                            \"title\": \"Card HTML pattern (HTML with variables, for ex. '${entityName}, ${entityLabel} or ${keyName} - some text.')\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"<div class='card-title'>\\n    ${entityName}\\n</div>\\n\\n<div class='card-data-label'>\\n    Sin\\n</div>\\n<div>\\n    ${Sin:1}\\n</div>\\n\\n<div class='card-data-label'>\\n    Cos\\n</div>\\n<div>\\n    ${Cos:2} <span class='card-data-unit'>°C</span>\\n</div>\"\r\n                        },\r\n                        \"cardStyleFunction\": {\r\n                            \"title\": \"Card style function: f(datasource, ctx)\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {};\"\r\n                        }\r\n                    },\r\n                    \"required\": [\r\n                        \"aliasName\",\r\n                        \"cardHtmlPattern\",\r\n                        \"cardStyleFunction\"\r\n                    ]\r\n                }\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"cardTemplates\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: A card template and a action of element click per entity alias used in the datasources.</small></I>\"\r\n        },\r\n        {\r\n            \"key\": \"cardTemplates\",\r\n            \"type\": \"array\",\r\n            \"items\": [\r\n                \"cardTemplates[].aliasName\",\r\n                {\r\n                    \"key\": \"cardTemplates[].cardHtmlPattern\",\r\n                    \"type\": \"html\"\r\n                },\r\n                {\r\n                    \"key\": \"cardTemplates[].cardStyleFunction\",\r\n                    \"type\": \"javascript\"\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, datasource, ctx)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value, datasource, ctx)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": [\n            \"useCellContentFunction\",\n            \"useCellStyleFunction\"]\n    },\n    \"form\": [\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\",\n            \"helpId\": \"widget/lib/entity/cell_content_fn\",\n            \"condition\": \"model.useCellContentFunction === true\"\n        },\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\",\n            \"helpId\": \"widget/lib/entity/cell_style_fn\",\n            \"condition\": \"model.useCellStyleFunction === true\"\n        }\n    ]\n}",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function1\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false,\"cellContentFunction\":\"return value;\"},\"_hash\":0.921001743574346,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7396784107489787,\"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;\",\"aggregationType\":null,\"units\":null,\"decimals\":2,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}},{\"type\":\"function\",\"name\":\"function2\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.197245861336796,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"useCellContentFunction\":true,\"cellContentFunction\":\"return value > 0 ? (\\\"\\\"+value+\\\", true\\\") : (\\\"\\\"+value+\\\", false\\\");\",\"cellStyleFunction\":\"let color_ = value > 0 ? \\\"Lime\\\" : \\\"Purple\\\";\\nreturn {\\n    color: color_\\n};\"},\"_hash\":0.9530188663404315,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":2,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardTemplates\":[{\"additionalInfoRequired\":false,\"cardHtmlPattern\":\"<div class='card-title'>\\n    ${entityName}! This is a testing of title!\\n</div>\\n<div> \\n    <span class=\\\"card-data-label\\\">Sin:</span> \\n    ${Sin:1}\\n</div>\\n<div>\\n    <span class=\\\"card-data-label\\\">Random:</span>\\n    ${Random:2}\\n</div>\\n<div>\\n    <span class=\\\"card-data-label\\\">Duplicate random:</span>\\n    ${Random:2}\\n</div>\",\"aliasName\":\"function1\",\"cardStyleFunction\":\"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {};\"},{\"cardHtmlPattern\":\"<div class='card-title'> \\n    ${entityName}\\n</div>\\n\\n<div class='card-data-label'>\\n    Sin\\n</div>\\n<div>\\n    ${Sin:1}\\n</div>\\n\\n<div class='card-data-label'>\\n    Cos. This a a testing of data label!\\n</div>\\n<div>\\n    ${Cos:2} <span class=\\\"card-data-unit\\\">°C</span>\\n</div>\",\"aliasName\":\"function2\",\"cardStyleFunction\":\"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {background: '#2db36a', color: 'white'};\"}]},\"title\":\"Entities Cards\",\"useDashboardTimewindow\":true,\"actions\":{\"elementClick\":[{\"name\":\"function1\",\"icon\":\"settings\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"alert(\\\"click function1\\\");\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"47834831-03d9-3202-0355-0b56baf200d3\"},{\"name\":\"function2\",\"icon\":\"my_location\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"alert(\\\"click function2\\\");\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"be41565a-831e-aae5-3ebe-ca27c194bfbd\"}]}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_time_value",
      "name": "Update time value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 3.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n    <form class=\"attribute-update-form\"\r\n          name=\"attrUpdateForm\"\r\n          (ngSubmit)=\"updateValue()\">\r\n          <div style=\"padding: 0 2px; margin: auto 0;\">\r\n            <div class=\"attribute-update-form__grid\" [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\" style=\"height: 26px;\">\r\n                <div class=\"grid__element\" style=\"horizontal-alignment:false; width: 50px;\">\r\n                    <mat-form-field class=\"mat-block\" style=\"width: 100%; height: 26px;\">\r\n\t\t\t\t\t    <input mat-timepicker matInput \r\n                               [(ngModel)]=\"currentValue\"\r\n                               required\r\n                               type=\"time\"\r\n\t\t\t\t\t\t\t   [ngModelOptions]=\"{standalone: true}\" />\r\n                        <mat-error>\r\n                            {{requiredErrorMessage}}\r\n                        </mat-error>\r\n                    </mat-form-field>\r\n                </div>\r\n\r\n                <div class=\"grid__element\" style=\"align-items:center; margin-top: 6px;\">\r\n                    <button mat-icon-button class=\"applyChanges\"\r\n                               type=\"submit\"\r\n                               [disabled]=\"(originalValue === currentValue) || (!currentValue)\"\r\n                               matTooltip=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\r\n                               matTooltipPosition=\"above\">\r\n                        <mat-icon>check</mat-icon>\r\n                    </button>\r\n                    <button mat-icon-button class=\"discardChanges\"\r\n                               type=\"button\"\r\n                               [disabled]=\"originalValue === currentValue\"\r\n                               (click)=\"currentValue = originalValue;\"\r\n                               matTooltip=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\r\n                               matTooltipPosition=\"above\">\r\n                        <mat-icon>close</mat-icon>\r\n                    </button>\r\n                </div>\r\n            </div>\r\n    \r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [fxHide]=\"entityDetected\" [innerHtml]=\"message\"></div>\r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                 [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n                {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n            </div>\r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                 [fxShow]=\"entityDetected && !isValidParameter\">\r\n                {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n            </div>\r\n        </div>\r\n    </form>\r\n</div>\r\n",
        "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-direction: column;\r\n    flex: 1;\r\n}\r\n.grid__element.horizontal-alignment {\r\n    flex-direction: row;\r\n}\r\n.grid__element:last-child {\r\n    align-items: center;\r\n    margin-left: 2px;\r\n}\r\n.grid__element {\r\n    display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n    margin: 0;\r\n    width: 24px;\r\n    min-width: 24px;\r\n    height: 24px;\r\n    min-height: 24px;\r\n    padding: 0 !important;\r\n    margin: 0 !important;\r\n    line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-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.attribute-update-form mdp-date-picker,\r\n.attribute-update-form mdp-time-picker {\r\n    width: 100%;\r\n}\r\n\r\n.attribute-update-form mdp-date-picker md-input-container,\r\n.attribute-update-form mdp-time-picker md-input-container {\r\n    margin: 5px 0 5px;\r\n    width: 100%;\r\n}\r\n\r\n.attribute-update-form grid__element .button.mat-icon-button {\r\n    margin: 2px 0 0;\r\n    padding: 0;\r\n    width: 22px;\r\n    height: 20px;\r\n}\r\n\r\n.attribute-update-form mat-form-field .mat-form-field-infix {\r\n    padding: 0;\r\n    border-top: .6em solid transparent;\r\n}\r\n\r\n.attribute-update-form.small-width mdp-date-picker md-input-container,\r\n.attribute-update-form.small-width mdp-time-picker md-input-container {\r\n    width: 78px;\r\n}\r\n\r\n.show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n",
        "controllerScript": "// v1.0.0, 2023-05-25 13:47\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant            ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n\r\n    ////self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    ////self.ctx.$scope.title = self.ctx.settings.title;\r\n    \r\n    /*function parseValue(data) {\r\n        //Using retrieve data: parse \"09:30\" to \"Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)\"\r\n        let result;\r\n        if (data && data.length>1 && typeof data === \"string\") {\r\n            var strarr = data.split(':', 3);\r\n            if (strarr && strarr.length>=2) {\r\n                var hour = Number(strarr[0]);\r\n                var min = Number(strarr[1]);\r\n                \r\n                if (hour>=0 && hour<=23 && min>=0 && min<=59) {\r\n                    var m = moment(\"1970-01-01 00:00:00.000\")\r\n                    m.hour(hour); \r\n                    m.minute(min); \r\n                    if (m.isValid()) {\r\n                        result = m.toDate();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        console.log(\"parseValue(): \", data, result);\r\n        return result;\r\n    }*/\r\n    let parseValueFunction = (data) => data; //parseValue;  // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) { \r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data; //parseValue;\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    let convertValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n\t\t\t$scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n\t\t\t$scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n\t    $scope.showErrorToast(translate.instant('retrieveRPCMethod is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        var newValue = convertValueFunction($scope.currentValue);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue && newValue.length){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    settings.updateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(settings.updateRPCMethod, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"retrieveMethod\",\r\n            \"updateMethod\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"showResultMessage\":true},\"title\":\"Update time value\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[],\"enableDataExport\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.segment_switch_of_boolean",
      "name": "Segment switch of boolean value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button (click)=\"updateValue(true)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n\t\t\t\t[color]=\"(true===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n\t\t\t\t[ngStyle]=\"true===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{options?.trueLabel}}\r\n            </button>\r\n            \r\n            <button mat-button (click)=\"updateValue(false)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n\t\t\t\t[color]=\"(false===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n\t\t\t\t[ngStyle]=\"false===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{options?.falseLabel}}\r\n            </button>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.optionStyle = settings.optionStyle;\r\n\r\n    self.ctx.$scope.customCheckedStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/2 + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.checkedOption.isPrimary === false) {\r\n        self.ctx.$scope.customCheckedStyle['background-color']  = self.ctx.$scope.optionStyle.checkedOption.bgColor;\r\n        self.ctx.$scope.customCheckedStyle['color']             = self.ctx.$scope.optionStyle.checkedOption.textColor;\r\n    }\r\n\r\n    self.ctx.$scope.customUncheckedStyle = {};\r\n    self.ctx.$scope.customUncheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customUncheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === \"column\") {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = 100/2 + '%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.uncheckedOption.isPrimary === false) {\r\n        self.ctx.$scope.customUncheckedStyle['background-color']  = self.ctx.$scope.optionStyle.uncheckedOption.bgColor;\r\n        self.ctx.$scope.customUncheckedStyle['color']             = self.ctx.$scope.optionStyle.uncheckedOption.textColor;\r\n    }\r\n    \r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    function translateToBoolean(updateValue) {\r\n        if (updateValue) {\r\n            if (typeof updateValue === \"string\") {\r\n                if (updateValue===\"true\" || updateValue===\"True\" || updateValue===\"TRUE\") {\r\n                    return true;\r\n                } else if (updateValue===\"false\" || updateValue===\"False\" || updateValue===\"FALSE\") {\r\n                    return false;\r\n                } \r\n            }/* else if (typeof updateValue === \"number\") {\r\n                return (updateValue===0) ? false : true;\r\n            }*/\r\n        }\r\n        \r\n        return updateValue;\r\n    }\r\n\r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;    //? true : false;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value; //? true : false;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(translateToBoolean(attrValue));  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(translateToBoolean(responseBody));\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(optionValue) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                optionValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                optionValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                optionValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve boolean value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns boolean\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return data; /* console.log(data); return data ? true : false; */ \"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value; /* console.log(value); return value ? true : false;*/ \"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n\r\n            \"options\": {\r\n                \"type\": \"object\",\r\n                \"title\": \"Options\",\r\n                \"properties\": {\r\n                    \"trueLabel\": {\r\n                        \"title\": \"True label\",\r\n                        \"type\": \"string\",\r\n                        \"default\": \"True label\"\r\n                    },\r\n                    \"falseLabel\": {\r\n                        \"title\": \"False label\",\r\n                        \"type\": \"string\",\r\n                        \"default\": \"False label\"\r\n                    }, \r\n                    \"required\": [\r\n                        \"falseLabel\",\r\n                        \"trueLabel\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": false\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"retrieveMethod\",\r\n                \"updateMethod\", \r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options.trueLabel\",\r\n                \"options.falseLabel\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"parseValueFunction\":\"/* console.log(data); */\\n/* return data ? true : false; */\\nreturn data;\",\"convertValueFunction\":\"/* console.log(value); */\\n/* return value ? true : false; */\\nreturn value;\",\"options\":{\"trueLabel\":\"True label\",\"falseLabel\":\"False label\"}},\"title\":\"Segment switch of boolean value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_with_state_parameter_from_flexiable_option",
      "name": "Select double value from flexiable option with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 4,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n    let patternParamOfRetrievedAttributeKey = settings.retrieveAttributes.patternParamOfRetrievedAttributeKey;\r\n    let retrievedAttributeKeyConvertFunc = (stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam) => patternParamOfRetrievedAttributeKey + patternCommonParam;\r\n    if (settings.retrieveAttributes.retrievedAttributeKeyConvertFunc && settings.retrieveAttributes.retrievedAttributeKeyConvertFunc.length) {\r\n        try {\r\n            retrievedAttributeKeyConvertFunc = new Function('stateParams', 'patternParamOfRetrievedAttributeKey', 'patternCommonParam', settings.retrieveAttributes.retrievedAttributeKeyConvertFunc);\r\n        } catch (e) {\r\n            retrievedAttributeKeyConvertFunc = (stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam) => patternParamOfRetrievedAttributeKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateAttributeKey = settings.patternParamOfUpdateAttributeKey;\r\n    let updateAttributeKeyConvertFunc = (stateParams, patternParamOfUpdateAttributeKey, patternCommonParam) => patternParamOfUpdateAttributeKey + patternCommonParam;\r\n    if (settings.updateAttributeKeyConvertFunc && settings.updateAttributeKeyConvertFunc.length) {\r\n        try {\r\n            updateAttributeKeyConvertFunc = new Function('stateParams', 'patternParamOfUpdateAttributeKey', 'patternCommonParam', settings.updateAttributeKeyConvertFunc);\r\n        } catch (e) {\r\n            updateAttributeKeyConvertFunc = (stateParams, patternParamOfUpdateAttributeKey, patternCommonParam) => patternParamOfUpdateAttributeKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateTimeseriesKey = settings.patternParamOfUpdateTimeseriesKey;\r\n    let updateTimeseriesKeyConvertFunc = (stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam) => patternParamOfUpdateTimeseriesKey + patternCommonParam;\r\n    if (settings.updateTimeseriesKeyConvertFunc && settings.updateTimeseriesKeyConvertFunc.length) {\r\n        try {\r\n            updateTimeseriesKeyConvertFunc = new Function('stateParams', 'patternParamOfUpdateTimeseriesKey', 'patternCommonParam', settings.updateTimeseriesKeyConvertFunc);\r\n        } catch (e) {\r\n            updateTimeseriesKeyConvertFunc = (stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam) => patternParamOfUpdateTimeseriesKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateRPCMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    let updateRPCMethodConvertNameFunc = (stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam) => patternParamOfUpdateRPCMethodName + patternCommonParam;\r\n    if (settings.updateRPCMethodConvertNameFunc && settings.updateRPCMethodConvertNameFunc.length) {\r\n        try {\r\n            updateRPCMethodConvertNameFunc = new Function('stateParams', 'patternParamOfUpdateRPCMethodName', 'patternCommonParam', settings.updateRPCMethodConvertNameFunc);\r\n        } catch (e) {\r\n            updateRPCMethodConvertNameFunc = (stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam) => patternParamOfUpdateRPCMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    /* //////////////////////////////////////////////////////////////////////////////////////////////////////////\r\n\tretrieveAttributes_adjusted = retrievedAttributeKeyConvertFunc(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam);\r\n\tupdateAttributeKey = updateAttributeKeyConvertFunc(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam);\r\n\tupdateTimeseriesKey = updateTimeseriesKeyConvertFunc(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam);\r\n\tupdateRPCMethod = updateRPCMethodConvertNameFunc(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam);\r\n\t///////////////////////////////////////////////////////////////////////////////////////////////////////////// */\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttrKey = {};\r\n    retrieveAttrKey.retrieved = retrievedAttributeKeyConvertFunc(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam); //settings.retrieveAttributes.retrieved; //let retrieveAttributeKey = settings.retrieveAttributeKey;\r\n    retrieveAttrKey.min      = settings.retrieveAttributes.min;     //let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    retrieveAttrKey.max      = settings.retrieveAttributes.max;     //let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    retrieveAttrKey.step     = settings.retrieveAttributes.step;    //let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    retrieveAttrKey.unit     = settings.retrieveAttributes.unit;    //let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    \r\n    \r\n    //console.log(\"retrievedAttributeKeyConvertFunc\", retrievedAttributeKeyConvertFunc);\r\n    //console.log(\"stateParams\", stateParams);\r\n    //console.log(\"patternParamOfRetrievedAttributeKey\", patternParamOfRetrievedAttributeKey);\r\n    //console.log(\"patternCommonParam\", patternCommonParam);\r\n    //console.log(\"retrieveAttrKey.retrieved\", retrieveAttrKey.retrieved);\r\n    //console.log(\"settings\", settings);\r\n    \r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue = {};\r\n\r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttrKey.retrieved && keyName === retrieveAttrKey.retrieved) {\r\n                    /*try { \r\n                        attrValue.updated = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.updated = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttrKey.min && keyName === retrieveAttrKey.min) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.min = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.max && keyName === retrieveAttrKey.max) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.max = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.step && keyName === retrieveAttrKey.step) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.step = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.unit && keyName === retrieveAttrKey.unit) {\r\n                    attrValue.unit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValue.min, attrValue.max, attrValue.step, attrValue.unit, attrValue.updated);\r\n        //console.log(lastAttrValue.min, lastAttrValue.max, lastAttrValue.step, lastAttrValue.unit);\r\n        if (attrValue.min !== lastAttrValue.min || attrValue.max !== lastAttrValue.max ||\r\n            attrValue.step !== lastAttrValue.step || attrValue.unit !== lastAttrValue.unit) {\r\n\r\n            if (attrValue.min)  lastAttrValue.min = attrValue.min;\r\n            if (attrValue.max)  lastAttrValue.max = attrValue.max;\r\n            if (attrValue.step) lastAttrValue.step = attrValue.step; \r\n            if (attrValue.unit) lastAttrValue.unit = attrValue.unit;\r\n            \r\n            if (attrValue.min && attrValue.max && attrValue.step) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValue.min;\r\n                let fMax = factor*attrValue.max;\r\n                let fStep = factor*attrValue.step;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValue.min, i=0; val<factor*attrValue.max+factor*attrValue.step; i++, val=factor*attrValue.min+factor*attrValue.step*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValue.unit && attrValue.unit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValue.unit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue.updated) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue.updated);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue.updated, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttrKey.retrieved && retrieveAttrKey.retrieved.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.retrieved } );\r\n            }\r\n            if (retrieveAttrKey.min && retrieveAttrKey.min.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.min } );\r\n            }\r\n            if (retrieveAttrKey.max && retrieveAttrKey.max.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.max } );\r\n            }\r\n            if (retrieveAttrKey.step && retrieveAttrKey.step.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.step } );\r\n            }\r\n            if (retrieveAttrKey.unit && retrieveAttrKey.unit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.unit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        //console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                updateAttributeKeyConvertFunc(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam), //settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                updateTimeseriesKeyConvertFunc(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam), //settings.updateTimeseriesKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(updateRPCMethodConvertNameFunc(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam), //settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"patternCommonParam\": {\n                \"title\": \"Pattern common param\",\n                \"type\": \"string\",\n                \"default\": \"0\"\n            },\n\n            \"retrieveAttributes\": {\n                \"type\": \"object\",\n                \"title\": \"Retrieve device attributes\",\n                \"properties\": {\n                    \"patternParamOfRetrievedAttributeKey\": {\n                        \"title\": \"Pattern param of retrieved device attribute key\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                        \"retrievedAttributeKeyConvertFunc\": {\n                            \"title\": \"Convert function, f(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam), returns retireved device attribute key\",\n                            \"type\": \"string\",\n                            \"default\": \"return patternParamOfRetrievedAttributeKey + patternCommonParam; /* console.log(value); */\"\n                        },\n                    \"min\": {\n                        \"title\": \"Min value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"max\": {\n                        \"title\": \"Max value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"step\": {\n                        \"title\": \"Step value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"unit\": {\n                        \"title\": \"Unit of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    }\n                },\n                \"required\": [\n                    \"patternParamOfRetrievedAttributeKey\",\n                        \"retrievedAttributeKeyConvertFunc\",\n                    \"min\",\n                    \"max\",\n                    \"step\",\n                    \"unit\"\n                ]\n            },\n\n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"patternParamOfUpdateAttributeKey\": {\n                \"title\": \"Pattern param of updated attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateAttributeKeyConvertFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam), returns updated attribute key\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateAttributeKey + patternCommonParam; /* console.log(value); */\"\n                },\n            \"patternParamOfUpdateTimeseriesKey\": {\n                \"title\": \"Pattern param of updated time-series data key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateTimeseriesKeyConvertFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam), returns updated time-series data key\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateTimeseriesKey + patternCommonParam; /* console.log(value); */\"\n                },\n            \"patternParamOfUpdateRPCMethodName\": {\n                \"title\": \"Pattern param of updated server-side RPC method name\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateRPCMethodConvertNameFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam), returns using server-side RPC method name\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateRPCMethodName + patternCommonParam; /* console.log(value); */\"\n                },\n            \n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert function of updated value, f(value), returns updated value\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"patternCommonParam\",\n            \"retrieveAttributes\",\n            \"updateMethod\",\n            \"convertValueFunction\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        \"patternCommonParam\",\n\n        \"retrieveAttributes.patternParamOfRetrievedAttributeKey\",\n            {\n                \"key\": \"retrieveAttributes.retrievedAttributeKeyConvertFunc\",\n                \"type\": \"javascript\"\n            },\n        \"retrieveAttributes.min\",\n        \"retrieveAttributes.max\",\n        \"retrieveAttributes.step\",\n        \"retrieveAttributes.unit\",\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update time-series data\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        {\n            \"key\": \"patternParamOfUpdateAttributeKey\",\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\n        },\n            {\n                \"key\": \"updateAttributeKeyConvertFunc\",\n                \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\n            \"condition\": \"model.updateMethod === 'timeseries'\"\n        },\n            {\n                \"key\": \"updateTimeseriesKeyConvertFunc\",\n                \"condition\": \"model.updateMethod === 'timeseries'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n            {\n                \"key\": \"updateRPCMethodConvertNameFunc\",\n                \"condition\": \"model.updateMethod === 'rpc'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"retrieveAttributes\":{\"adjusted\":\"adjust\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethod\":\"setValue\"},\"title\":\"Select double value from flexiable option with pattern key\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_timezone_value",
      "name": "Select timezone value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\n//[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\"\r\n\r\nlet timezones = [   {\"name\":\"UTC-12:00\", \"offset\": -(12*60)},\r\n                    {\"name\":\"UTC-11:00\", \"offset\": -(11*60)},\r\n                    {\"name\":\"UTC-10:00\", \"offset\": -(10*60)},\r\n                    {\"name\":\"UTC-09:30\", \"offset\": -( 9*60 + 30)},\r\n                    {\"name\":\"UTC-09:00\", \"offset\": -( 9*60)},\r\n                    {\"name\":\"UTC-08:00\", \"offset\": -( 8*60)},\r\n                    {\"name\":\"UTC-07:00\", \"offset\": -( 7*60)},\r\n                    {\"name\":\"UTC-06:00\", \"offset\": -( 6*60)},\r\n                    {\"name\":\"UTC-05:00\", \"offset\": -( 5*60)},\r\n                    {\"name\":\"UTC-04:00\", \"offset\": -( 4*60)},\r\n                    {\"name\":\"UTC-03:30\", \"offset\": -( 3*60 + 30)},\r\n                    {\"name\":\"UTC-03:00\", \"offset\": -( 3*60)},\r\n                    {\"name\":\"UTC-02:00\", \"offset\": -( 2*60)},\r\n                    {\"name\":\"UTC-01:00\", \"offset\": -( 1*60)},\r\n                    {\"name\":\"UTC\",       \"offset\":  ( 0*60)},\r\n                    {\"name\":\"UTC+00:00\", \"offset\":  ( 0*60)},\r\n                    {\"name\":\"UTC+01:00\", \"offset\":  ( 1*60)},\r\n                    {\"name\":\"UTC+02:00\", \"offset\":  ( 2*60)},\r\n                    {\"name\":\"UTC+03:00\", \"offset\":  ( 3*60)},\r\n                    {\"name\":\"UTC+03:30\", \"offset\":  ( 3*60 + 30)},\r\n                    {\"name\":\"UTC+04:00\", \"offset\":  ( 4*60)},\r\n                    {\"name\":\"UTC+04:30\", \"offset\":  ( 4*60 + 30)},\r\n                    {\"name\":\"UTC+05:00\", \"offset\":  ( 5*60)},\r\n                    {\"name\":\"UTC+05:30\", \"offset\":  ( 5*60 + 30)},\r\n                    {\"name\":\"UTC+06:00\", \"offset\":  ( 6*60)},\r\n                    {\"name\":\"UTC+06:30\", \"offset\":  ( 6*60 + 30)},\r\n                    {\"name\":\"UTC+07:00\", \"offset\":  ( 7*60)},\r\n                    {\"name\":\"UTC+08:00\", \"offset\":  ( 8*60)},\r\n                    {\"name\":\"UTC+09:00\", \"offset\":  ( 9*60)},\r\n                    {\"name\":\"UTC+09:30\", \"offset\":  ( 9*60 + 30)},\r\n                    {\"name\":\"UTC+10:00\", \"offset\":  (10*60)},\r\n                    {\"name\":\"UTC+10:30\", \"offset\":  (10*60 + 30)},\r\n                    {\"name\":\"UTC+11:00\", \"offset\":  (11*60)},\r\n                    {\"name\":\"UTC+12:00\", \"offset\":  (12*60)},\r\n                    {\"name\":\"UTC+12:45\", \"offset\":  (12*60 + 45)},\r\n                    {\"name\":\"UTC+13:00\", \"offset\":  (13*60)},\r\n                    {\"name\":\"UTC+14:00\", \"offset\":  (14*60)},\r\n                ];\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveMethod\": {\n                \"title\": \"Retrieve timezone(integer value) using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrieveAttributeKey\": {\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveRPCMethod\": {\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"parseValueFunction\": {\n                \"title\": \"Parse value function, f(data), returns timezone(integer value)\",\n                \"type\": \"string\",\n                \"default\": \"return data; /* console.log(data); */ \"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update timezone(integer value) using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n\n            \"required\": [\n                \"retrieveMethod\",\n                \"updateMethod\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrieveMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC get value method\"\n                }\n            ]\n        },\n        \"retrieveAttributeKey\",\n        \"retrieveRPCMethod\",\n        {\n            \"key\": \"parseValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Select timezone value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.testattributescard",
      "name": "Simple Attributes Card",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 7,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "",
        "templateCss": "#container {\r\n    overflow: auto;\r\n}\r\n\r\n.tbDatasource-container {\r\n    margin: 4px;\r\n    padding: 1px;\r\n}\r\n\r\n.tbDatasource-title {\r\n    font-size: 1.200rem;\r\n    font-weight: 500;\r\n    padding-bottom: 10px;\r\n}\r\n\r\n.tbDatasource-table {\r\n    width: 100%;\r\n    box-shadow: 0 0 10px #ccc;\r\n    border-collapse: collapse;\r\n    white-space: nowrap;\r\n    font-size: 1.000rem;\r\n    color: #757575;\r\n}\r\n\r\n.tbDatasource-table td {\r\n    position: relative;\r\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\r\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\r\n    padding: 0px 9px;\r\n    box-sizing: border-box;\r\n}",
        "controllerScript": "// Based on Cards-->Attributes Card\r\n\r\nself.onInit = function() {\r\n    \r\n    //self.ctx.datasourceTitleCells = [];\r\n    self.ctx.valueCells = [];\r\n    self.ctx.labelCells = [];\r\n    \r\n    for (var i=0; i < self.ctx.datasources.length; i++) {\r\n        var tbDatasource = self.ctx.datasources[i];\r\n\r\n        var datasourceId = 'tbDatasource' + i;\r\n        self.ctx.$container.append(\r\n            \"<div id='\" + datasourceId +\r\n            \"' class='tbDatasource-container'></div>\"\r\n        );\r\n\r\n        var datasourceContainer = $('#' + datasourceId,\r\n            self.ctx.$container);\r\n\r\n        //datasourceContainer.append(\r\n        //    \"<div class='tbDatasource-title'>\" +\r\n        //    tbDatasource.name + \"</div>\"\r\n        //);\r\n        \r\n        //var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\r\n        //self.ctx.datasourceTitleCells.push(datasourceTitleCell);\r\n        \r\n        var tableId = 'table' + i;\r\n        datasourceContainer.append(\r\n            \"<table id='\" + tableId +\r\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\r\n        );\r\n        var table = $('#' + tableId, self.ctx.$container);\r\n\r\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\r\n            var dataKey = tbDatasource.dataKeys[a];\r\n            var labelCellId = 'labelCell' + a;\r\n            var cellId = 'cell' + a;\r\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\r\n                \"</td><td id='\" + cellId +\r\n                \"'></td></tr>\");\r\n            var labelCell = $('#' + labelCellId, table);\r\n            self.ctx.labelCells.push(labelCell);\r\n            var valueCell = $('#' + cellId, table);\r\n            self.ctx.valueCells.push(valueCell);\r\n        }\r\n    }    \r\n    \r\n    self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\r\n        var cellData = self.ctx.data[i];\r\n        if (cellData && cellData.data && cellData.data.length > 0) {\r\n            var tvPair = cellData.data[cellData.data.length -\r\n                1];\r\n            var value = tvPair[1];\r\n            var textValue;\r\n            //toDo -> + IsNumber\r\n            \r\n            if (isNumber(value)) {\r\n                var decimals = self.ctx.decimals;\r\n                var units = self.ctx.units;\r\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\r\n                    decimals = cellData.dataKey.decimals;\r\n                }\r\n                if (cellData.dataKey.units) {\r\n                    units = cellData.dataKey.units;\r\n                }\r\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\r\n            } else {\r\n                txtValue = value;\r\n            }\r\n            self.ctx.valueCells[i].html(txtValue);\r\n        }\r\n    }\r\n    \r\n    function isNumber(n) {\r\n        return !isNaN(parseFloat(n)) && isFinite(n);\r\n    }\r\n}\r\n\r\nself.onResize = function() {\r\n    var datasourceTitleFontSize = self.ctx.height/8;\r\n    if (self.ctx.width/self.ctx.height <= 1.5) {\r\n        datasourceTitleFontSize = self.ctx.width/12;\r\n    }\r\n    datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\r\n    //for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\r\n    //    self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\r\n    //}\r\n    var valueFontSize = self.ctx.height/9;\r\n    var labelFontSize = self.ctx.height/9;\r\n    if (self.ctx.width/self.ctx.height <= 1.5) {\r\n        valueFontSize = self.ctx.width/15;\r\n        labelFontSize = self.ctx.width/15;\r\n    }\r\n    valueFontSize = Math.min(valueFontSize, 18);\r\n    labelFontSize = Math.min(labelFontSize, 18);\r\n\r\n    for (i = 0; i < self.ctx.valueCells; i++) {\r\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\r\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\r\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize*0.5 + 'px');\r\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\r\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\r\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize*0.5 + 'px');\r\n    }    \r\n}\r\n\r\nself.onDestroy = function() {\r\n}\r\n\r\nself.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: -1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    };\r\n}\r\n",
        "settingsSchema": "{}",
        "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;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.5242256504485792,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.5818890099680771,\"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\":\"Simple Attributes Card\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.segment_switch_of_boolean",
      "name": "Segment switch of boolean value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button (click)=\"updateValue(true)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n\t\t\t\t[color]=\"(true===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n\t\t\t\t[ngStyle]=\"true===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{options?.trueLabel}}\r\n            </button>\r\n            \r\n            <button mat-button (click)=\"updateValue(false)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n\t\t\t\t[color]=\"(false===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n\t\t\t\t[ngStyle]=\"false===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{options?.falseLabel}}\r\n            </button>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.optionStyle = settings.optionStyle;\r\n\r\n    self.ctx.$scope.customCheckedStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/2 + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.checkedOption.isPrimary === false) {\r\n        self.ctx.$scope.customCheckedStyle['background-color']  = self.ctx.$scope.optionStyle.checkedOption.bgColor;\r\n        self.ctx.$scope.customCheckedStyle['color']             = self.ctx.$scope.optionStyle.checkedOption.textColor;\r\n    }\r\n\r\n    self.ctx.$scope.customUncheckedStyle = {};\r\n    self.ctx.$scope.customUncheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customUncheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === \"column\") {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = 100/2 + '%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.uncheckedOption.isPrimary === false) {\r\n        self.ctx.$scope.customUncheckedStyle['background-color']  = self.ctx.$scope.optionStyle.uncheckedOption.bgColor;\r\n        self.ctx.$scope.customUncheckedStyle['color']             = self.ctx.$scope.optionStyle.uncheckedOption.textColor;\r\n    }\r\n    \r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    function translateToBoolean(updateValue) {\r\n        if (updateValue) {\r\n            if (typeof updateValue === \"string\") {\r\n                if (updateValue===\"true\" || updateValue===\"True\" || updateValue===\"TRUE\") {\r\n                    return true;\r\n                } else if (updateValue===\"false\" || updateValue===\"False\" || updateValue===\"FALSE\") {\r\n                    return false;\r\n                } \r\n            }/* else if (typeof updateValue === \"number\") {\r\n                return (updateValue===0) ? false : true;\r\n            }*/\r\n        }\r\n        \r\n        return updateValue;\r\n    }\r\n\r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;    //? true : false;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value; //? true : false;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(translateToBoolean(attrValue));  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(translateToBoolean(responseBody));\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(optionValue) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                optionValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                optionValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                optionValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve boolean value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns boolean\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return data; /* console.log(data); return data ? true : false; */ \"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value; /* console.log(value); return value ? true : false;*/ \"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n\r\n            \"options\": {\r\n                \"type\": \"object\",\r\n                \"title\": \"Options\",\r\n                \"properties\": {\r\n                    \"trueLabel\": {\r\n                        \"title\": \"True label\",\r\n                        \"type\": \"string\",\r\n                        \"default\": \"True label\"\r\n                    },\r\n                    \"falseLabel\": {\r\n                        \"title\": \"False label\",\r\n                        \"type\": \"string\",\r\n                        \"default\": \"False label\"\r\n                    }, \r\n                    \"required\": [\r\n                        \"falseLabel\",\r\n                        \"trueLabel\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": false\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"retrieveMethod\",\r\n                \"updateMethod\", \r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options.trueLabel\",\r\n                \"options.falseLabel\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"parseValueFunction\":\"/* console.log(data); */\\n/* return data ? true : false; */\\nreturn data;\",\"convertValueFunction\":\"/* console.log(value); */\\n/* return value ? true : false; */\\nreturn value;\",\"options\":{\"trueLabel\":\"True label\",\"falseLabel\":\"False label\"}},\"title\":\"Segment switch of boolean value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.tabs_navigation_bar",
      "name": "Tabs navigation bar",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 9.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"my-tbs-navigation\" fxFlex fxLayout=\"column\"\r\n    style=\"height: 100%;\"\r\n    fxLayoutAlign=\"space-around stretch\">\r\n\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"center center\">\r\n\r\n        <mat-tab-group mat-stretch-tabs \r\n             [headerPosition]=\"position\" [attr.mat-align-tabs]=\"align\"\r\n             [color]=\"color\" [backgroundColor]=\"bgColor\"\r\n            animationDuration=\"0ms\"\r\n            (selectedTabChange)=\"onSelectedTabChange($event.index)\"\r\n            selectedIndex=1>\r\n            <mat-tab *ngFor=\"let tab of tabs\">\r\n                <ng-template mat-tab-label>\r\n                    <div *ngIf=\"tab.icon\">\r\n                        <mat-icon class=\"example-tab-icon\">\r\n                            {{tab.icon}}</mat-icon>\r\n                    </div>{{tab.label}}\r\n                </ng-template>\r\n            </mat-tab>\r\n        </mat-tab-group>\r\n    </div>\r\n\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".my-tbs-navigation {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.my-tbs-navigation .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 10px;\r\n}\r\n\r\n.my-tbs-navigation .button-container{\r\n    min-width: 80%\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-tab-group {\r\n    width: 100%;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-tab-label {\r\n    padding: 0 2px;\r\n    min-width: 80px\r\n}\r\n\r\n.my-tbs-navigation .button-container .example-tab-icon{\r\n  margin-right: 0px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n    padding: 0 0px;\r\n}\r\n\r\n.my-tbs-navigation .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.my-tbs-navigation .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "//v.1.0.0, 2023-05-23 11:43\r\n\r\nself.onInit = function() {\r\n    self.ctx.$scope.onSelectedTabChange = function(indexTabs) {\r\n        //console.log(indexTabs);  //window.alert(\"select \" + indexTabs);\r\n        //console.log(self.ctx);\r\n        if (!self.ctx.isEdit && self.ctx.stateController) {\r\n            let toStatedId = self.ctx.$scope.tabs[indexTabs].dashboardState;\r\n\r\n            let currentState = self.ctx.stateController.getStateId();\r\n            if (currentState !== toStatedId) {\r\n                //self.ctx.stateController.getStateParams(): StateParams;\r\n                //self.ctx.stateController.getStateParamsByStateId(stateId: string): StateParams;\r\n                var params = {};\r\n                self.ctx.stateController.updateState(toStatedId, params, false);\r\n            }\r\n        }\r\n    };\r\n\r\n    ////self.ctx.ngZone.run(function() {\r\n       init();\r\n    //  self.ctx.detectChanges(true);\r\n    //});\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    //let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    //let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    //let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    //let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.position = settings.position;\r\n    self.ctx.$scope.align = settings.align;\r\n    self.ctx.$scope.minWidth = settings.minWidth;\r\n    //self.ctx.$scope.isPrimary = settings.isPrimary; //\r\n    self.ctx.$scope.color = settings.color; //\r\n    self.ctx.$scope.bgColor = settings.bgColor; //\r\n    self.ctx.$scope.tabs = settings.tabs;\r\n\r\n    /*self.ctx.$scope.tabGroupStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.isPrimary === false) {\r\n        self.ctx.$scope.tabGroupStyle['background-color']  = self.ctx.$scope.bgColor;\r\n        self.ctx.$scope.tabGroupStyle['color']             = self.ctx.$scope.color;\r\n    }*/\r\n\r\n    //console.log(\"onInit\", self.ctx.$scope.align, self.ctx.$scope.position);\r\n}\r\n\r\nself.onResize = function() {\r\n    //    min-width: 80%\r\n};\r\n\r\nself.onDestory = function() {\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"position\": {\r\n                \"title\": \"Text position\",\r\n                \"type\": \"string\",\r\n                \"default\": \"above\"\r\n            },\r\n            \"align\": {\r\n                \"title\": \"Align\",\r\n                \"type\": \"string\",\r\n                \"default\": \"center\"\r\n            },\r\n            \"minWidth\": {\r\n                \"title\": \"Min width(e.g., 100px)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"100px\"\r\n            },\r\n            \"color\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Tabs color\",\r\n                \"default\": \"\"\r\n            },\r\n            \"bgColor\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Tabs background color\",\r\n                \"default\": \"\"\r\n            },\r\n            \"tabs\":{\r\n                \"type\": \"array\",\r\n                \"title\": \"Tabs\",\r\n                \"minItems\": 1,\r\n                \"maxItems\": 30,\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"title\": \"Tab\",\r\n                    \"properties\": {\r\n                        \"dashboardState\": {\r\n                            \"title\": \"Dashboard state\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"default\"\r\n                        },\r\n                        \"label\": {\r\n                            \"title\": \"Tab label\",\r\n                            \"type\": \"string\",\r\n                            \"description\": \"\"\r\n                        },\r\n                        \"icon\": {\r\n                            \"title\": \"Tab icon, e.g., ac_unit, timer, settings and build.\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"\"\r\n                        }\r\n                    }, \r\n                    \"required\": [\r\n                        \"label\",\r\n                        \"dashboardState\"\r\n                    ]\r\n                }\r\n            }\r\n        },\r\n            \r\n        \"required\": [\r\n            \"position\",\r\n            \"align\",\r\n            \"minWidth\",\r\n            \"tabs\"\r\n        ]\r\n    },\r\n\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"position\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"above\",\r\n                    \"label\": \"above\"\r\n                },\r\n                {\r\n                    \"value\": \"below\",\r\n                    \"label\": \"below\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"align\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"start\",\r\n                    \"label\": \"start\"\r\n                },\r\n                {\r\n                    \"value\": \"center\",\r\n                    \"label\": \"center\"\r\n                },\r\n                {\r\n                    \"value\": \"end\",\r\n                    \"label\": \"end\"\r\n                }\r\n            ]\r\n        },\r\n        \"minWidth\",\r\n        {\r\n            \"key\": \"bgColor\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        },\r\n        {\r\n            \"key\": \"color\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        },\r\n        {\r\n            \"key\": \"tabs\",\r\n            \"items\": [\r\n                \"tabs[]\",\r\n                {\r\n                    \"type\": \"help\",\r\n                    \"description\": \"Refer to https://mui.com/material-ui/material-icons for more information about material-icons.\"\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"position\":\"above\",\"align\":\"center\",\"tabs\":[{\"dashboardState\":\"first\",\"tabLabel\":\"First\",\"label\":\"First\",\"icon\":\"ac_unit\"},{\"dashboardState\":\"second\",\"tabLabel\":\"Second\",\"label\":\"Second\"},{\"dashboardState\":\"third\",\"label\":\"Third\",\"icon\":\"settings\"}],\"minWidth\":\"200px\"},\"title\":\"Tabs navigation bar\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.simple_state_param_card",
      "name": "Simple state params card",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"center center\">\r\n\r\n        <div class='card'>\r\n            <div class='content'>\r\n                {{displayText}}\r\n            </div>\r\n        </div>\r\n\r\n    </div>\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container{\r\n    min-width: 80%\r\n}\r\n\r\n.card {\r\n    font-weight: bold;\r\n    font-size: 32px;\r\n    color: #999;\r\n    width: 100%;\r\n    height: 100%;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n}\r\n\r\n.card .content{\r\n    font-size: 22px;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\n// let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    //self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    //self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let stateParamsConvertFunc = (stateParams) => \"State param\";\r\n    if (settings.stateParamsConvertFunc && settings.stateParamsConvertFunc.length) {\r\n        try {\r\n            stateParamsConvertFunc = new Function('stateParams', settings.stateParamsConvertFunc);\r\n        } catch (e) {\r\n            stateParamsConvertFunc = (stateParams) => \"State param\";\r\n        }\r\n    }\r\n\r\n    //console.log(\"stateParamsConvertFunc\", stateParamsConvertFunc);\r\n    //console.log(\"stateParams\", stateParams);\r\n    //console.log(\"settings\", settings);\r\n\r\n    self.ctx.$scope.displayText = stateParamsConvertFunc(stateParams);\r\n}\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    //if (valueSubscription) {\r\n    //    self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    //}\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"stateParamsConvertFunc\": {\n                \"title\": \"Convert function, f(stateParams), returns display text\",\n                \"type\": \"string\",\n                \"default\": \"return 'State param'; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"stateParamsConvertFunc\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"stateParamsConvertFunc\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"patternCommonParam\":\"0\",\"retrieveAttributes\":{\"retrievedAttributeKeyConvertFunc\":\"return patternParamOfRetrievedAttributeKey + patternCommonParam; /* console.log(value); */\",\"patternParamOfRetrievedAttributeKey\":\"value\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethodConvertNameFunc\":\"return patternParamOfUpdateRPCMethodName + patternCommonParam; /* console.log(value); */\"},\"title\":\"Simple state params card\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.html_attributes_cards",
      "name": "Entities Cards",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 9,
        "sizeY": 4,
        "resources": [],
        "templateHtml": "",
        "templateCss": ".card-container {\n    width: 100%;\n    /*height: fit-content;*/ /*height: 100%;*/\n    \n    padding-left: inherit;\n    padding-right: inherit;\n    \n    /*box-sizing: border-box;*/\n    /*border: #696969 1px solid;*/\n    \n\t/* 要创建一个 flex 容器，只需要将一个 display: flex 属性添加到一个元素上。\n\t默认情况下，所有的直接子元素都被认为是 flex 项，并从左到右依次排列在一行中。\n\t如果 flex 项的宽度总和大于容器，那么 flex 项将按比例缩小，直到它们适应 flex 容器宽度 */\n\tdisplay: flex;\n\t/* flex-direction 决定主轴的方向 row(默认)|row-reverse|column|column-reverse*/\n\tflex-direction: row;\n\t/* flex-wrap决定当排列不下时是否换行以及换行的方式,nowrap(默认)|wrap|wrap-reverse */\n\tflex-wrap:wrap;\n\t/* flex-flow是lex-direction和flex-wrap的简写形式，如：row wrap|column wrap-reverse等。默认值为row nowrap，即横向排列 不换行 */\n\tflex-flow:row wrap;\n\t/* !当主轴沿水平方向时!justify-content,决定item在主轴上的对齐方式，可能的值有flex-start（默认），flex-end，center，space-between，space-around */\n\tjustify-content: space-around;\n\t/* !主轴水平时!决定了item在交叉轴上的对齐方式，可能的值有flex-start|flex-end|center|baseline|stretch */\n\talign-items: flex-start;\n}\n\n.card-container .card {\n    margin: .3em;\n    \n    padding: 5px;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    /*height: 100%;*/\n    box-sizing: border-box;\n    transition: background-color 0.5s;\n    \n    width: 160px;\n    height: 10em;\n\n    background-color: #f1f1f1;\n    /*box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);*/\n    box-shadow: 0px 10px 20px -13px rgba(16, 16, 16, 0.35);\n    border-radius: 3px;\n\n    text-align: center;\n\n    /*background: #2db36a;\n    color: #ffffff;*/\n}\n\n/* Style the counter cards */\n/*.card-container .card {\n  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);\n  padding: 16px;\n  text-align: center;\n  background-color: #f1f1f1;\n}*/\n\n.card-container .card .card-title {\n    /*color: #ffffff;\n    padding-bottom: 1em;\n    text-align: center;\n    opacity: 0.9;*/\n    \n    /*font-size: 1.200rem;\n    font-weight: 500;*/\n    font-size: 1.2em;\n    opacity: 0.9;\n    padding-bottom: 5px;\n}\n\n/*.card-container .card .card-line{\n    text-align: center;\n}*/\n\n.card-container .card .card-data-label{\n    /*color: #ffffff;*/\n    font-size: 0.8em;\n    opacity: 0.7;\n    /*text-align: center;*/\n}\n\n.card-container .card .card-data-pure-value {\n    /*color: #ffffff;*/\n    font-size: 1.6em;\n    font-weight: 500;\n}\n\n.card-container .card .card-data-unit {\n    /*color: #ffffff;*/\n    font-size: .6em;\n}\n\n#container {\n    overflow: auto;\n}\n\n\n.tbDatasource-container {\n    margin: 5px;\n    padding: 8px;\n}\n\n.tbDatasource-title {\n    font-size: 1.200rem;\n    font-weight: 500;\n    padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n    width: 100%;\n    box-shadow: 0 0 10px #ccc;\n    border-collapse: collapse;\n    white-space: nowrap;\n    font-size: 1.000rem;\n    color: #757575;\n}\n\n.tbDatasource-table td {\n    position: relative;\n    border-top: 1px solid rgba(0, 0, 0, 0.12);\n    border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n    padding: 0px 18px;\n    box-sizing: border-box;\n}",
        "controllerScript": "//Avantec Manufacture Limited, V1.0.0, 20230705\nself.onInit = function() {\n    var global_cell_id = 111;\n\n    self.ctx.$container.append(\n        \"<div class='card-container'></div>\"\n    );\n    var cardContainer = $('.card-container', self.ctx.$container);\n\n    /* self.ctx.datasourceTitleCells = []; */\n    /* self.ctx.valueCells = []; \n    self.ctx.labelCells = []; */\n\n    self.ctx.cells = [];\n\n    let utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n    let startIndexOfData = 0;\n    for (var i=0; i < self.ctx.datasources.length; i++) {\n\n        // append a card per datasource\n        var tbDatasource = self.ctx.datasources[i];\n        var datasourceId = 'tbDatasource' + i;\n        // self.ctx.$container.append(\n        //     \"<div id='\" + datasourceId +\n        //     \"' class='tbDatasource-container'></div>\"\n        // );\n        cardContainer.append(\n            \"<div id='\" + datasourceId +\n            \"' class='card' class='mat-primary' data-data-source-alias-name='\"\n            + tbDatasource.aliasName +\n            \"' data-id='\" + tbDatasource.entityId +\n            \"' data-entity-type='\" + tbDatasource.entityType + \n            \"' data-entity-name='\" + tbDatasource.entityName + \n            \"' data-entity-label='\" + tbDatasource.entityLabel + \n            \"'></div>\"\n        );\n        var datasourceContainer = $('#' + datasourceId,\n            self.ctx.$container);\n        datasourceContainer.click((event) => {\n            self.onCardClick(event);\n        });\n         \n        // append a title on the top of a card\n        /* datasourceContainer.append(\n            \"<div class='tbDatasource-title'>\" +\n            tbDatasource.name + \"</div>\"\n        );\n        var datasourceTitleCell = $('.tbDatasource-title',\n            datasourceContainer);\n        self.ctx.datasourceTitleCells.push(datasourceTitleCell); */\n\n        /*\n        // append a table in a card\n        var tableId = 'table' + i;\n        datasourceContainer.append(\n            \"<table id='\" + tableId +\n            \"' class='tbDatasource-table'><col width='30%'><col width='70%'></table>\"\n        );\n        var table = $('#' + tableId, self.ctx.$container);\n        // append some tr/td for all data keys\n        for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n            var dataKey = tbDatasource.dataKeys[a];\n            var labelCellId = 'labelCell' + a;\n            var cellId = 'cell' + a;\n            table.append(\"<tr><td id='\" + labelCellId + \"'>\" + dataKey.label +\n                \"</td><td id='\" + cellId +\n                \"'></td></tr>\");\n            var labelCell = $('#' + labelCellId, table);\n            self.ctx.labelCells.push(labelCell);\n            var valueCell = $('#' + cellId, table);\n            self.ctx.valueCells.push(valueCell);\n        } */\n\n        // append some cells from datasource\n        const aliasName = tbDatasource.aliasName;\n        if (self.ctx.settings.cardTemplates) {\n            // look up a template for a aliasName\n            let template = self.ctx.settings.cardTemplates.find(\n                    template => template.aliasName === aliasName);\n            //console.log(\"template=\", template);\n            if (template) {\n                let pattern = utils.createLabelFromDatasource(\n                    tbDatasource, template.cardHtmlPattern);\n                //console.log(\"pattern=\", pattern);\n                if (pattern && self.ctx.datasources && self.ctx.datasources.length) {\n                    createCellsFromPattern(datasourceContainer, self.ctx.cells, \n                        pattern, tbDatasource, startIndexOfData);\n                }\n                \n                try {\n                    //let styleFunction = (datasource, ctx) => {return {};};\n                    if (template.cardStyleFunction && template.cardStyleFunction.length) {\n                        let styleFunction = new Function(\n                            'datasource', 'ctx', template.cardStyleFunction);\n                        let css = styleFunction(tbDatasource, self.ctx);\n                        if (css) {\n                            datasourceContainer.css(css);\n                            //datasourceContainer.css('background', '#2db36a');\n                        }\n                    }\n                } catch (ex) {\n                    console.log(ex);\n                }\n            }\n        }\n        \n        startIndexOfData += tbDatasource.dataKeys.length;\n    }    \n    \n    //console.log(\"self.ctx\", self.ctx);\n    //console.log(\"self.ctx.settings\", self.ctx.settings);\n    //console.log(\"self.ctx.datasources\", self.ctx.datasources);\n    /* console.log(\"self.ctx.datasourceTitleCells[0]\", self.ctx.datasourceTitleCells[0]); */\n    \n    self.onResize();\n\n    function createCellsFromPattern(container, cells, pattern, datasource,\n            startIndexOfData) {\n        const replaceInfo = processDataPattern(pattern, datasource);\n        //console.log(\"replaceInfo=\", replaceInfo);\n        fillDataPattern(container, cells, pattern, replaceInfo, datasource,\n            startIndexOfData);\n    }\n    function fillDataPattern(container, cells, pattern, replaceInfo, datasource,\n            startIndexOfData) {\n        let ids = [];\n        let text = pattern; //utils.createLabelFromDatasource(datasource, pattern);\n        if (replaceInfo) {\n            for (const variableInfo of replaceInfo) {\n                // let txtVal = '';\n                // if (variableInfo.dataKeyName && isDefinedAndNotNull(data[variableInfo.dataKeyName]))\n                // {\n                //     const varData = data[variableInfo.dataKeyName];\n                //     if (isNumber(varData)) {\n                //         txtVal = padValue(varData, variableInfo.valDec);\n                //     } else {\n                //         txtVal = varData;\n                //     }\n                // }\n                // text = text.replace(variableInfo.variable, txtVal);\n                \n                let index = getDataKeyIndexFromDataSource(\n                                variableInfo.dataKeyName, datasource);\n                if (isDefinedAndNotNull(index)) {\n                    index += startIndexOfData;\n                    let cellId = \"cell_id_\"+global_cell_id; global_cell_id +=1;\n                    let txtVal =   \"<span id='\" + cellId + \"' class='card-data-pure-value'></span>\";\n                    text = text.replace(variableInfo.variable, txtVal);\n                    ids.push({cellId:cellId, index:index});\n                    //console.log(cellId, index);\n                }\n            }\n        }\n        container.append(text);\n        ids.forEach(item => {\n            var valueCell = $('#' + item.cellId, container);\n            cells.push({index:item.index, valueCell:valueCell});\n        });\n        \n        //console.log(\"ids=\", ids);\n    }\n    function processDataPattern(pattern, datasource) {\n        const replaceInfo = [];\n        try {\n            const reg = /\\${([^}]*)}/g;\n            let match = reg.exec(pattern);\n            while (match !== null) {\n                const variableInfo = {\n                    dataKeyName: '',\n                    valDec: 2,\n                    variable: ''\n                };\n                const variable = match[0];\n                let label = match[1];\n                let valDec = 2;\n                const splitValues = label.split(':');\n                if (splitValues.length > 1) {\n                    label = splitValues[0];\n                    valDec = parseFloat(splitValues[1]);\n                }\n        \n                variableInfo.variable = variable;\n                variableInfo.valDec = valDec;\n        \n                if (label.startsWith('#')) {\n                    const keyIndexStr = label.substring(1);\n                    const n = Math.floor(Number(keyIndexStr));\n                    if (String(n) === keyIndexStr && n >= 0) {\n                        variableInfo.dataKeyName = datasource.dataKeys[n].label;\n                    }\n                } else {\n                    variableInfo.dataKeyName = label;\n                }\n                replaceInfo.push(variableInfo);\n\n                match = reg.exec(pattern);\n            }\n        } catch (ex) {\n            console.log(ex, pattern);\n        }\n        return replaceInfo;\n    }\n    function getDataKeyIndexFromDataSource(datakeyLabel, datasource) {\n        let index;\n        try {\n            index  = datasource.dataKeys.findIndex(\n                (dataKey) => dataKey.label === datakeyLabel);\n            //console.log(\"datakeyLabel=\", datakeyLabel, index);\n        } catch (ex) {\n            console.log(ex);\n        }\n        return index;\n    }\n    function isDefinedAndNotNull(value) {\n        return typeof value !== 'undefined' && value !== null;\n    }\n};\n\nself.onCardClick= function(event) { //Element\n    //console.log(\"event=\", event); //alert(\"1\");\n\n    const element = event.currentTarget; // event.target || event.srcElement;\n    if (event && element.dataset && element.dataset.id) {\n        const dataSourceAliasName = element.dataset.dataSourceAliasName || '';\n        const id_ = element.dataset.id ? element.dataset.id : null\n            , entityType_ = element.dataset.entityType ? element.dataset.entityType : null\n            , entityId = {entityType: entityType_, id: id_}\n            , entityName = element.dataset.entityName ? element.dataset.entityName : null\n            , entityLabel = element.dataset.entityLabel ? element.dataset.entityLabel : null;\n        // const entityInfo = self.ctx.actionsApi.getActiveEntityInfo()\n        //     , entityId = entityInfo ? entityInfo.entityId : null\n        //     , entityName = entityInfo ? entityInfo.entityName : null\n        //     , entityLabel = entityInfo && entityInfo.entityLabel ? entityInfo.entityLabel : null;\n\n        // self.ctx.actionsApi.getActionDescriptors(actionSourceId);\n        let actionDescriptors = self.ctx.actionsApi.getActionDescriptors(\"elementClick\");\n    \n        //console.log(\"self.ctx=\", self.ctx);\n        //console.log(\"dataSourceAliasName=\", dataSourceAliasName);\n        //console.log(\"actionDescriptors=\", actionDescriptors);\n    \n        //console.log(\"entityInfo=\", entityInfo);\n        //console.log(\"entityId=\", entityId);\n        //console.log(\"entityName=\", entityName);\n        //console.log(\"entityLabel=\", entityLabel);\n    \n        if (actionDescriptors.length) {\n            const descriptor = actionDescriptors.find(actionDescriptor => actionDescriptor.name === dataSourceAliasName);\n            //console.log(\"descriptor=\", descriptor);\n            if (descriptor) {\n                event.stopPropagation();\n                // self.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName);\n                self.ctx.actionsApi.handleWidgetAction(event, descriptor, entityId, entityName, null, entityLabel);\n            }\n        }\n    } else {\n        \n    }\n};\n\nself.onDataUpdated = function() {\n    /*\n    for (var i = 0; i < self.ctx.valueCells.length; i++) {\n        var cellData = self.ctx.data[i];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length - 1];\n            var value = tvPair[1];\n            var txtValue;\n            //toDo -> + IsNumber\n            \n            if (isNumber(value)) {\n                var decimals = self.ctx.decimals;\n                var units = self.ctx.units;\n                if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n                    decimals = cellData.dataKey.decimals;\n                }\n                if (cellData.dataKey.units) {\n                    units = cellData.dataKey.units;\n                }\n                txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n            } else {\n                txtValue = value;\n            }\n            self.ctx.valueCells[i].html(txtValue);\n        }\n    } */\n    \n    // new TC\n    //console.log(\"self.ctx.cells=\", self.ctx.cells);\n    for (var i = 0; i < self.ctx.cells.length; i++) {\n        var cellData = self.ctx.data[self.ctx.cells[i].index];\n        if (cellData && cellData.data && cellData.data.length > 0) {\n            var tvPair = cellData.data[cellData.data.length - 1];\n            var value = tvPair[1];\n            //toDo -> + IsNumber\n\n            // update HTML\n            var txtValue;\n            if (cellData.dataKey.settings.useCellContentFunction) {\n                txtValue = getCellContentFromFunction(value,\n                    cellData.datasource, self.ctx,\n                    cellData.dataKey.settings.cellContentFunction);\n            } else {\n                txtValue = getCellContent(value, cellData.dataKey.decimals,\n                    cellData.dataKey.units, self.ctx);\n            }\n            self.ctx.cells[i].valueCell.html(txtValue);\n            \n            // update CSS\n            if (cellData.dataKey.settings.useCellStyleFunction) {\n                //console.log(\"cellData.dataKey.settings.useCellStyleFunction=\",\n                //  cellData.dataKey.settings.useCellStyleFunction);\n                let txtStyle = getCellStyleFromFunction(value,\n                        cellData.datasource, self.ctx,\n                        cellData.dataKey.settings.cellStyleFunction);\n                self.ctx.cells[i].valueCell.css(txtStyle);\n                //console.log(\"txtStyle=\", txtStyle);\n            }\n        }\n    }\n    \n    function getCellContent(value, decimals_, units_, ctx) {\n        let txtValue = \"\";\n        if (isNumber(value)) {\n            let decimals = ctx.decimals;\n            let units = ctx.units;\n            if (decimals_ || decimals_ === 0) {\n                decimals = decimals_;\n            }\n            if (units_) {\n                units = units_;\n            }\n            txtValue = ctx.utils.formatValue(value, decimals, units, true);\n        } else {\n            txtValue = value;\n            try {\n                if (typeof value === 'undefined' || value === null || typeof value === \"string\" && value.length==0) {\n                  txtValue = \"&nbsp;\";\n                }\n            } catch (ex) {\n                console.log(ex);\n            }\n            //console.log(\"value=\", value, \"value.length=\", value.length, \"txtValue=\", txtValue);\n        }        \n        return txtValue;\n    }\n    function getCellContentFromFunction(value, tbDatasource, ctx, cellContentFunction) {\n        let txtValue = \"\";\n        try {\n            if (cellContentFunction && cellContentFunction.length) {\n                let contentFunction = new Function('value',\n                    'datasource', 'ctx', cellContentFunction);\n                txtValue = contentFunction(value, tbDatasource, ctx);\n            }\n        } catch (ex) {\n            console.log(ex);\n        }\n        return txtValue;\n    }\n    function getCellStyleFromFunction(value, tbDatasource, ctx, cellStyleFunction) {\n        let txtStyle = {};\n        try {\n            if (cellStyleFunction && cellStyleFunction.length) {\n                let styleFunction = new Function('value',\n                    'datasource', 'ctx', cellStyleFunction);\n                txtStyle = styleFunction(value, tbDatasource, ctx);\n            }\n        } catch (ex) {\n            console.log(ex);\n        }\n        return txtStyle;\n    }\n    \n    function isNumber(n) {\n        return !isNaN(parseFloat(n)) && isFinite(n);\n    }\n};\n\nself.onResize = function() {\n    /* var datasourceTitleFontSize = self.ctx.height/8;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        datasourceTitleFontSize = self.ctx.width/12;\n    }\n    datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n    for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n        self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n    } */\n    var valueFontSize = self.ctx.height/9;\n    var labelFontSize = self.ctx.height/9;\n    if (self.ctx.width/self.ctx.height <= 1.5) {\n        valueFontSize = self.ctx.width/15;\n        labelFontSize = self.ctx.width/15;\n    }\n    valueFontSize = Math.min(valueFontSize, 18);\n    labelFontSize = Math.min(labelFontSize, 18);\n\n    /*\n    for (i = 0; i < self.ctx.valueCells; i++) {\n        self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n        self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n        self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n        self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n        self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n        self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n    }*/\n};\n\n// self.typeParameters = function() {\n//     // TODO:\n// };\n\nself.actionSources = function() {\n    return {\n        'elementClick': {\n            name: 'widget-action.element-click',\n            multiple: true\n        }\n    };\n};\n\nself.onDestroy = function() {\n};\n\n    // function isNumber(value) {\n    //     return typeof value === 'number';\n    // }\n    // function padValue(val, dec) {\n    //     let strVal;\n    //     let n;\n        \n    //     val = parseFloat(val);\n    //     n = (val < 0);\n    //     val = Math.abs(val);\n        \n    //     if (dec > 0) {\n    //         strVal = val.toFixed(dec);\n    //     } else {\n    //         strVal = Math.round(val).toString();\n    //     }\n    //     strVal = (n ? '-' : '') + strVal;\n    //     return strVal;\n    // }\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"cardTemplates\": {\r\n                \"type\": \"array\",\r\n                \"title\": \"Card templates\",\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"aliasName\": {\r\n                            \"type\": \"string\",\r\n                            \"title\": \"Alias Name\",\r\n                            \"minLength\": 1\r\n                        },\r\n                        \"cardHtmlPattern\": {\r\n                            \"title\": \"Card HTML pattern (HTML with variables, for ex. '${entityName}, ${entityLabel} or ${keyName} - some text.')\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"<div class='card-title'>\\n    ${entityName}\\n</div>\\n\\n<div class='card-data-label'>\\n    Sin\\n</div>\\n<div>\\n    ${Sin:1}\\n</div>\\n\\n<div class='card-data-label'>\\n    Cos\\n</div>\\n<div>\\n    ${Cos:2} <span class='card-data-unit'>°C</span>\\n</div>\"\r\n                        },\r\n                        \"cardStyleFunction\": {\r\n                            \"title\": \"Card style function: f(datasource, ctx)\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {};\"\r\n                        }\r\n                    },\r\n                    \"required\": [\r\n                        \"aliasName\",\r\n                        \"cardHtmlPattern\",\r\n                        \"cardStyleFunction\"\r\n                    ]\r\n                }\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"cardTemplates\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: A card template and a action of element click per entity alias used in the datasources.</small></I>\"\r\n        },\r\n        {\r\n            \"key\": \"cardTemplates\",\r\n            \"type\": \"array\",\r\n            \"items\": [\r\n                \"cardTemplates[].aliasName\",\r\n                {\r\n                    \"key\": \"cardTemplates[].cardHtmlPattern\",\r\n                    \"type\": \"html\"\r\n                },\r\n                {\r\n                    \"key\": \"cardTemplates[].cardStyleFunction\",\r\n                    \"type\": \"javascript\"\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, datasource, ctx)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value, datasource, ctx)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": [\n            \"useCellContentFunction\",\n            \"useCellStyleFunction\"]\n    },\n    \"form\": [\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\",\n            \"helpId\": \"widget/lib/entity/cell_content_fn\",\n            \"condition\": \"model.useCellContentFunction === true\"\n        },\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\",\n            \"helpId\": \"widget/lib/entity/cell_style_fn\",\n            \"condition\": \"model.useCellStyleFunction === true\"\n        }\n    ]\n}",
        "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function1\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false,\"cellContentFunction\":\"return value;\"},\"_hash\":0.921001743574346,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7396784107489787,\"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;\",\"aggregationType\":null,\"units\":null,\"decimals\":2,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}},{\"type\":\"function\",\"name\":\"function2\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.197245861336796,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"useCellContentFunction\":true,\"cellContentFunction\":\"return value > 0 ? (\\\"\\\"+value+\\\", true\\\") : (\\\"\\\"+value+\\\", false\\\");\",\"cellStyleFunction\":\"let color_ = value > 0 ? \\\"Lime\\\" : \\\"Purple\\\";\\nreturn {\\n    color: color_\\n};\"},\"_hash\":0.9530188663404315,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"aggregationType\":null,\"units\":null,\"decimals\":2,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardTemplates\":[{\"additionalInfoRequired\":false,\"cardHtmlPattern\":\"<div class='card-title'>\\n    ${entityName}! This is a testing of title!\\n</div>\\n<div> \\n    <span class=\\\"card-data-label\\\">Sin:</span> \\n    ${Sin:1}\\n</div>\\n<div>\\n    <span class=\\\"card-data-label\\\">Random:</span>\\n    ${Random:2}\\n</div>\\n<div>\\n    <span class=\\\"card-data-label\\\">Duplicate random:</span>\\n    ${Random:2}\\n</div>\",\"aliasName\":\"function1\",\"cardStyleFunction\":\"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {};\"},{\"cardHtmlPattern\":\"<div class='card-title'> \\n    ${entityName}\\n</div>\\n\\n<div class='card-data-label'>\\n    Sin\\n</div>\\n<div>\\n    ${Sin:1}\\n</div>\\n\\n<div class='card-data-label'>\\n    Cos. This a a testing of data label!\\n</div>\\n<div>\\n    ${Cos:2} <span class=\\\"card-data-unit\\\">°C</span>\\n</div>\",\"aliasName\":\"function2\",\"cardStyleFunction\":\"//return { \\n//  fontWeight: 'bold', \\n//  background: '#2db36a',\\n//  color: '#ffffff'// 'green' \\n//};\\nreturn {background: '#2db36a', color: 'white'};\"}]},\"title\":\"Entities Cards\",\"useDashboardTimewindow\":true,\"actions\":{\"elementClick\":[{\"name\":\"function1\",\"icon\":\"settings\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"alert(\\\"click function1\\\");\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"47834831-03d9-3202-0355-0b56baf200d3\"},{\"name\":\"function2\",\"icon\":\"my_location\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"alert(\\\"click function2\\\");\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"be41565a-831e-aae5-3ebe-ca27c194bfbd\"}]}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.simple_state_param_card",
      "name": "Simple state params card",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"center center\">\r\n\r\n        <div class='card'>\r\n            <div class='content'>\r\n                {{displayText}}\r\n            </div>\r\n        </div>\r\n\r\n    </div>\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container{\r\n    min-width: 80%\r\n}\r\n\r\n.card {\r\n    font-weight: bold;\r\n    font-size: 32px;\r\n    color: #999;\r\n    width: 100%;\r\n    height: 100%;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n}\r\n\r\n.card .content{\r\n    font-size: 22px;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\n// let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    //self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    //self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let stateParamsConvertFunc = (stateParams) => \"State param\";\r\n    if (settings.stateParamsConvertFunc && settings.stateParamsConvertFunc.length) {\r\n        try {\r\n            stateParamsConvertFunc = new Function('stateParams', settings.stateParamsConvertFunc);\r\n        } catch (e) {\r\n            stateParamsConvertFunc = (stateParams) => \"State param\";\r\n        }\r\n    }\r\n\r\n    //console.log(\"stateParamsConvertFunc\", stateParamsConvertFunc);\r\n    //console.log(\"stateParams\", stateParams);\r\n    //console.log(\"settings\", settings);\r\n\r\n    self.ctx.$scope.displayText = stateParamsConvertFunc(stateParams);\r\n}\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    //if (valueSubscription) {\r\n    //    self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    //}\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"stateParamsConvertFunc\": {\n                \"title\": \"Convert function, f(stateParams), returns display text\",\n                \"type\": \"string\",\n                \"default\": \"return 'State param'; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"stateParamsConvertFunc\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"stateParamsConvertFunc\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"patternCommonParam\":\"0\",\"retrieveAttributes\":{\"retrievedAttributeKeyConvertFunc\":\"return patternParamOfRetrievedAttributeKey + patternCommonParam; /* console.log(value); */\",\"patternParamOfRetrievedAttributeKey\":\"value\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethodConvertNameFunc\":\"return patternParamOfUpdateRPCMethodName + patternCommonParam; /* console.log(value); */\"},\"title\":\"Simple state params card\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_with_state_parameter_from_flexiable_option2",
      "name": "Select double value from flexiable option (New)",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 8,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '5px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\"> \r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-weight: 500;\r\n    white-space: nowrap;\r\n    margin: 10px 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 4px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}",
        "controllerScript": "// v.1.0.0, 2023-05-23 11:52\r\n\r\n// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttrKey = {};\r\n    retrieveAttrKey.adjusted = settings.retrieveAttributes.adjusted;//let retrieveAttributeKey            = settings.retrieveAttributeKey;\r\n    retrieveAttrKey.min      = settings.retrieveAttributes.min;     //let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    retrieveAttrKey.max      = settings.retrieveAttributes.max;     //let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    retrieveAttrKey.step     = settings.retrieveAttributes.step;    //let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    retrieveAttrKey.unit     = settings.retrieveAttributes.unit;    //let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    \r\n    //console.log(\"settings\", settings);\r\n    \r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue = {};\r\n\r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttrKey.adjusted && keyName === retrieveAttrKey.adjusted) {\r\n                    /*try { \r\n                        attrValue.adjusted = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.adjusted = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttrKey.min && keyName === retrieveAttrKey.min) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.min = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.max && keyName === retrieveAttrKey.max) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.max = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.step && keyName === retrieveAttrKey.step) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.step = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.unit && keyName === retrieveAttrKey.unit) {\r\n                    attrValue.unit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValue.min, attrValue.max, attrValue.step, attrValue.unit, attrValue.adjusted);\r\n        //console.log(lastAttrValue.min, lastAttrValue.max, lastAttrValue.step, lastAttrValue.unit);\r\n        if (attrValue.min !== lastAttrValue.min || attrValue.max !== lastAttrValue.max ||\r\n            attrValue.step !== lastAttrValue.step || attrValue.unit !== lastAttrValue.unit) {\r\n\r\n            if (attrValue.min)  lastAttrValue.min = attrValue.min;\r\n            if (attrValue.max)  lastAttrValue.max = attrValue.max;\r\n            if (attrValue.step) lastAttrValue.step = attrValue.step; \r\n            if (attrValue.unit) lastAttrValue.unit = attrValue.unit;\r\n            \r\n            if (attrValue.min && attrValue.max && attrValue.step) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValue.min;\r\n                let fMax = factor*attrValue.max;\r\n                let fStep = factor*attrValue.step;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValue.min, i=0; val<factor*attrValue.max+factor*attrValue.step; i++, val=factor*attrValue.min+factor*attrValue.step*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValue.unit && attrValue.unit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValue.unit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue.adjusted) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue.adjusted);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue.adjusted, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttrKey.adjusted && retrieveAttrKey.adjusted.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.adjusted } );\r\n            }\r\n            if (retrieveAttrKey.min && retrieveAttrKey.min.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.min } );\r\n            }\r\n            if (retrieveAttrKey.max && retrieveAttrKey.max.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.max } );\r\n            }\r\n            if (retrieveAttrKey.step && retrieveAttrKey.step.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.step } );\r\n            }\r\n            if (retrieveAttrKey.unit && retrieveAttrKey.unit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.unit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateTimeseriesKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"retrieveAttributes\": {\n                \"type\": \"object\",\n                \"title\": \"Retrieve device attributes\",\n                \"properties\": {\n                    \"adjusted\": {\n                        \"title\": \"Adjusted device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"min\": {\n                        \"title\": \"Min value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"max\": {\n                        \"title\": \"Max value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"step\": {\n                        \"title\": \"Step value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"unit\": {\n                        \"title\": \"Unit of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    }\n                },\n                \"required\": [\n                    \"adjusted\",\n                    \"min\",\n                    \"max\",\n                    \"step\",\n                    \"unit\"\n                ]\n            },\n\n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Key of attribute being updated\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateTimeseriesKey\": {\n                \"title\": \"Key of timeseries value being updated(?)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using client-side RPC method\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"retrieveAttributes\",\n            \"updateMethod\",\n            \"convertValueFunction\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        \"retrieveAttributes\",\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [{\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        {\n            \"key\": \"updateAttributeKey\",\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\n        },\n        {\n            \"key\": \"updateTimeseriesKey\",\n            \"condition\": \"model.updateMethod === 'timeseries'\"\n        },\n        {\n            \"key\": \"updateRPCMethod\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"retrieveAttributes\":{\"adjusted\":\"adjusted\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethod\":\"setValue\",\"updateAttributeKey\":\"value\"},\"title\":\"Select double value from flexiable option (New)\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"enableDataExport\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.styled_button_of_string_value_with_pattern_key",
      "name": "Styled button of string value with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 10,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\"\r\n    style=\"width: 100%; height: 100%;\">\r\n\r\n    <div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n        <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n            class=\"title-container\" fxLayout=\"row\"\r\n            fxLayoutAlign=\"start center\"\r\n            [fxShow]=\"showTitle\">\r\n            <span class=\"button-title\">{{title}}</span>\r\n        </div>\r\n\r\n        <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n            class=\"button-container\" fxLayout=\"column\"\r\n            fxLayoutAlign=\"center center\">\r\n\r\n                <button mat-button (click)=\"updateValue(buttonState?.on.value===currentValue)\"\r\n                    [class.mat-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                    [class.mat-mdc-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                    [color]=\"(buttonState?.on.value===currentValue ? buttonState?.on.isPrimary : buttonState?.off.isPrimary) ? 'primary' : ''\" \r\n                    [ngStyle]=\"buttonState?.on.value===currentValue ? customOnStyle : customOffStyle\">\r\n                    {{buttonState?.on.value===currentValue ? buttonState?.on.label : buttonState?.off.label}}\r\n                </button>\r\n\r\n        </div>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n/*TC 20230529 for mat-mdc-raised-button*/\r\n.tb-rpc-button .button-container .mat-mdc-raised-button {\r\n    width: 100%;\r\n}\r\n\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\r\n.tb-rpc-button .show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n",
        "controllerScript": "// v.1.0.0, TC 2023-05-29\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n    self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    self.ctx.$scope.title = self.ctx.settings.title;\r\n    self.ctx.$scope.buttonState = settings.buttonState;\r\n    if (settings.buttonState.on.isPrimary === false) {\r\n            self.ctx.$scope.customOnStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.on.bgColor,\r\n                'color': self.ctx.$scope.buttonState.on.textColor,\r\n            };\r\n    }\r\n    if (settings.buttonState.off.isPrimary === false) {\r\n            self.ctx.$scope.customOffStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.off.bgColor,\r\n                'color': self.ctx.$scope.buttonState.off.textColor,\r\n            };\r\n    }\r\n\r\n    //console.log(\"onInit\", settings);\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n\r\n    function parseValue(data) {\r\n        var parsed = data;\r\n        if (typeof settings.buttonState.on.value === \"string\") {\r\n            if (typeof data === \"boolean\") {\r\n                //parsed = data===true ? \"true\" : \"false\";\r\n                parsed = data===true ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            } else if (typeof data === \"number\") {\r\n                //parsed = data===1 ? \"true\" : \"false\";\r\n                parsed = data!==0 ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            }\r\n        } else if (typeof settings.buttonState.on.value === \"boolean\") {\r\n            if (typeof data === \"string\") {\r\n                parsed = data===\"true\" ? true : false;\r\n            } else if (typeof data === \"number\") {\r\n                parsed = data!==0 ? true : false;\r\n            }\r\n        }\r\n        return parsed;\r\n    }\r\n\r\n    let patternParamOfetrievedAttributeKeyOrMethodName = \" \";\r\n    if (settings.retrieveMethod === \"attribute\" || settings.retrieveMethod === \"SHARED_SCOPE\" || settings.retrieveMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveAttributeKey;\r\n    } else if (settings.retrieveMethod === \"timeseries\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveTimeseriesKey;\r\n    } else if (settings.retrieveMethod === \"rpc\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveRPCMethodName;\r\n    }\r\n\r\n    let convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertRetrievedAttributeKeyOrMethodNameFunction && settings.convertRetrievedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfetrievedAttributeKeyOrMethodName', 'patternCommonParam', settings.convertRetrievedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let retrievedAttributeKeyOrMethodName = convertRetrievedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertRetrievedValueFunction = parseValue; //(value) => value;  // Retrieve data\r\n    if (settings.convertRetrievedValueFunction && settings.convertRetrievedValueFunction.length) {\r\n        try {\r\n            convertRetrievedValueFunction = new Function('value', settings.convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = parseValue; //(value) => value；\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    \r\n    let patternParamOfUpdateAttributeKeyOrMethodName = \" \";\r\n    if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateAttributeKey;\r\n    } else if (settings.updateMethod === \"timeseries\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateTimeseriesKey;\r\n    } else if (settings.updateMethod === \"rpc\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    }\r\n\r\n    let convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertUpdatedAttributeKeyOrMethodNameFunction && settings.convertUpdatedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfUpdateAttributeKeyOrMethodName', 'patternCommonParam', settings.convertUpdatedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let updateAttributeKeyOrMethodName = convertUpdatedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertUpdatedValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertUpdatedValueFunction && settings.convertUpdatedValueFunction.length) {\r\n        try {\r\n            convertUpdatedValueFunction = new Function('value', settings.convertUpdatedValueFunction);\r\n        } catch (e) {\r\n            convertUpdatedValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                ////if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                ////}\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n\r\n    /*function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }*/\r\n\r\n    function subscribeAttributes(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n\r\n        //if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [\r\n        //        {name: key}\r\n        //    ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    function subscribeTimeseries(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [\r\n        //        {name: key}\r\n        //    ];\r\n        //} else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrievedAttributeKeyOrMethodName is null.\";\r\n            $scope.showErrorToast(translate.instant('retrievedAttributeKeyOrMethodName is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(on_off) {\r\n        //console.log(\"updateValue()\", settings);\r\n\r\n        let value = on_off ? settings.buttonState.off.value : \r\n            settings.buttonState.on.value;\r\n        let newValue = convertUpdatedValueFunction(value);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateTimeseriesKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateRPCMethodName, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(retrievedAttributeKeyOrMethodName, settings.requestTimeout); //settings.patternParamOfRetrieveRPCMethodName\r\n                } else if (settings.retrieveMethod == 'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveAttributeKey\r\n                    }\r\n                } else if (settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveTimeseriesKey\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"patternCommonParam\": {\r\n                \"title\": \"Pattern common param\",\r\n                \"type\": \"string\",\r\n                \"default\": \"0\"\r\n            },\r\n\r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfRetrieveAttributeKey\": {\r\n                \"title\": \"Pattern param of retrieved Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveTimeseriesKey\": {\r\n                \"title\": \"Pattern param of retrieved Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveRPCMethodName\": {\r\n                \"title\": \"Pattern param of retrieved value using server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\": {\r\n                \"title\": \"Convert function, f(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam), returns retireved attribute key, time-series data key or server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n            },\r\n            \"convertRetrievedValueFunction\": {\r\n                \"title\": \"Parse value function, f(value), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfUpdateAttributeKey\": {\r\n                \"title\": \"Pattern param of updated Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateTimeseriesKey\": {\r\n                \"title\": \"Pattern param of updated Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateRPCMethodName\": {\r\n                \"title\": \"Pattern param of updated server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\": {\r\n                \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam), returns updated attribute key, time-series data key or server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n            },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"convertUpdatedValueFunction\": {\r\n                \"title\": \"Convert function, f(value), returns updated value\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"buttonState\": {\r\n                \"type\": \"object\",\r\n                \"title\": \"Button state\",\r\n                \"properties\": {\r\n                    \"on\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"On state\",\r\n                        \"properties\": {\r\n                            \"value\": {\r\n                                \"title\": \"On state attribute value\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"true\",\r\n                                \"description\": \"On state attribute value.\"\r\n                            },\r\n                            \"label\": {\r\n                                \"title\": \"On state label\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"On state Label\"\r\n                            },\r\n                            \"isRaised\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Raised\",\r\n                                \"default\": true\r\n                            },\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"On state background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"On state text color\",\r\n                                \"default\": null\r\n                            }\r\n                        },\r\n                        \"required\": [\r\n                            \"value\",\r\n                            \"label\"\r\n                        ]\r\n                    },\r\n                    \"off\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Off state\",\r\n                        \"properties\": {\r\n                            \"value\": {\r\n                                \"title\": \"Off state attribute value\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"false\",\r\n                                \"description\": \"Off state attribute value.\"\r\n                            },\r\n                            \"label\": {\r\n                                \"title\": \"Off state label\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"Off state label\"\r\n                            },\r\n                            \"isRaised\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Raised\",\r\n                                \"default\": true\r\n                            },\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": false\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Off state background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Off state text color\",\r\n                                \"default\": null\r\n                            }\r\n                        },\r\n                        \"required\": [\r\n                            \"value\",\r\n                            \"label\"\r\n                        ]\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"on\",\r\n                    \"off\"\r\n                ]\r\n            },\r\n            \"showResultMessage\": {\r\n                \"title\": \"Show result message\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"patternCommonParam\",\r\n            \"retrieveMethod\",\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"updateMethod\",\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n            \"requestTimeout\",\r\n            \"buttonState\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"patternCommonParam\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [{\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-dide RPC to get value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveAttributeKey\",\r\n            \"condition\": \"model.retrieveMethod === 'attribute' || model.retrieveMethod === 'SHARED_SCOPE' || model.retrieveMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveTimeseriesKey\",\r\n            \"condition\": \"model.retrieveMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveRPCMethodName\",\r\n            \"condition\": \"model.retrieveMethod === 'rpc'\"\r\n        },\r\n        {\r\n            \"key\": \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"key\": \"convertRetrievedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\r\n        },\r\n\r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [{\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-side RPC to set value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateAttributeKey\",\r\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\r\n            \"condition\": \"model.updateMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\r\n            \"condition\": \"model.updateMethod === 'rpc'\"\r\n        },\r\n        {\r\n            \"key\": \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"key\": \"convertUpdatedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\r\n        },\r\n\r\n        \"requestTimeout\",\r\n\r\n        {\r\n            \"key\": \"buttonState\",\r\n            \"items\": [{\r\n                \"key\": \"buttonState.on\",\r\n                \"items\": [\r\n                    \"buttonState.on.value\",\r\n                    \"buttonState.on.label\",\r\n                    \"buttonState.on.isRaised\",\r\n                    \"buttonState.on.isPrimary\",\r\n                    {\r\n                        \"key\": \"buttonState.on.bgColor\",\r\n                        \"type\": \"color\"\r\n                    }, {\r\n                        \"key\": \"buttonState.on.textColor\",\r\n                        \"type\": \"color\"\r\n                    }\r\n                ]\r\n            }, {\r\n                \"key\": \"buttonState.off\",\r\n                \"items\": [\r\n                    \"buttonState.off.value\",\r\n                    \"buttonState.off.label\",\r\n                    \"buttonState.off.isRaised\",\r\n                    \"buttonState.off.isPrimary\",\r\n                    {\r\n                        \"key\": \"buttonState.off.bgColor\",\r\n                        \"type\": \"color\"\r\n                    }, {\r\n                        \"key\": \"buttonState.off.textColor\",\r\n                        \"type\": \"color\"\r\n                    }\r\n                ]\r\n            }]\r\n        },\r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"requestTimeout\":5000,\"retrieveMethod\":\"rpc\",\"updateMethod\":\"rpc\",\"buttonState\":{\"on\":{\"label\":\"On state Label\",\"isRaised\":true,\"isPrimary\":true,\"value\":\"True\"},\"off\":{\"value\":\"false\",\"label\":\"Off state label\",\"isRaised\":true,\"isPrimary\":false}},\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\"},\"title\":\"Styled button of string value with pattern key\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.styled_button_of_string_value",
      "name": "Styled button of string value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n            <button mat-button (click)=\"updateValue(buttonState?.on.value===currentValue)\"\r\n                [class.mat-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                [color]=\"(buttonState?.on.value===currentValue ? buttonState?.on.isPrimary : buttonState?.off.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"buttonState?.on.value===currentValue ? customOnStyle : customOffStyle\">\r\n                {{buttonState?.on.value===currentValue ? buttonState?.on.label : buttonState?.off.label}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\n    width: 100%;\n    height: 100%;\n}\n\n.tb-rpc-button .title-container {\n    font-size: 0.8em;\n    font-weight: normal;\n    white-space: nowrap;\n    opacity: 0.5;\n    margin: 0;\n}\n\n.tb-rpc-button .button-container div{\n    min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n    width: 100%;\n    margin: 0;\n}\n\n.tb-rpc-button .button-container .mat-button{\n    padding: 0 0px;\n}\n\n/*TC 20230529 for mat-mdc-raised-button*/\n.tb-rpc-button .button-container .mat-mdc-raised-button {\n    width: 100%;\n}\n\n.tb-rpc-button .error-container {\n    position: absolute;\n    top: 2%;\n    right: 0;\n    left: 0;\n    z-index: 4;\n    height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n    color: #ff3315;\n    white-space: nowrap;\n}",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.buttonState = settings.buttonState;\r\n    if (settings.buttonState.on.isPrimary === false) {\r\n            self.ctx.$scope.customOnStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.on.bgColor,\r\n                'color': self.ctx.$scope.buttonState.on.textColor,\r\n            };\r\n    }\r\n    if (settings.buttonState.off.isPrimary === false) {\r\n            self.ctx.$scope.customOffStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.off.bgColor,\r\n                'color': self.ctx.$scope.buttonState.off.textColor,\r\n            };\r\n    }\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    function parseValue(data) {\r\n        var parsed = data;\r\n        if (typeof settings.buttonState.on.value === \"string\") {\r\n            if (typeof data === \"boolean\") {\r\n                //parsed = data===true ? \"true\" : \"false\";\r\n                parsed = data===true ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            } else if (typeof data === \"number\") {\r\n                //parsed = data===1 ? \"true\" : \"false\";\r\n                parsed = data!==0 ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            }\r\n        } else if (typeof settings.buttonState.on.value === \"boolean\") {\r\n            if (typeof data === \"string\") {\r\n                parsed = data===\"true\" ? true : false;\r\n            } else if (typeof data === \"number\") {\r\n                parsed = data!==0 ? true : false;\r\n            }\r\n        }\r\n        return parsed;\r\n    }\r\n    let parseValueFunction = parseValue; //(data) => data;  // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = parseValue; //(data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": value    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(on_off) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        let value = on_off ? settings.buttonState.off.value : \r\n            settings.buttonState.on.value;\r\n        let converted = convertValueFunction(value);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                converted);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                converted);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                converted, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveMethod\": {\n                \"title\": \"Retrieve string value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrieveAttributeKey\": {\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveRPCMethod\": {\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"parseValueFunction\": {\n                \"title\": \"Parse value function, f(data), returns string\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update string value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \n            \"buttonState\":{\n                \"type\": \"object\",\n                \"title\": \"Button state\",\n                \"properties\": {\n                    \"on\": {\n                        \"type\": \"object\",\n                        \"title\": \"On state\",\n                        \"properties\": {\n                            \"value\": {\n                                \"title\": \"On state attribute value\",\n                                \"type\": \"string\",\n                                \"default\": \"true\",\n                                \"description\": \"On state attribute value.\"\n                            },\n                            \"label\": {\n                                \"title\": \"On state label\",\n                                \"type\": \"string\",\n                                \"default\": \"On state Label\"\n                            },\n                            \"isRaised\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Raised\",\n                                \"default\": true\n                            },\n                            \"isPrimary\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Primary color\",\n                                \"default\": true\n                            },\n                            \"bgColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"On state background color\",\n                                \"default\": null\n                            },\n                            \"textColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"On state text color\",\n                                \"default\": null\n                            }\n                        }, \n                        \"required\": [\n                            \"value\",\n                            \"label\"\n                        ]\n                    },\n                    \"off\": {\n                        \"type\": \"object\",\n                        \"title\": \"Off state\",\n                        \"properties\": {\n                            \"value\": {\n                                \"title\": \"Off state attribute value\",\n                                \"type\": \"string\",\n                                \"default\": \"false\",\n                                \"description\": \"Off state attribute value.\"\n                            },\n                            \"label\": {\n                                \"title\": \"Off state label\",\n                                \"type\": \"string\",\n                                \"default\": \"Off state label\"\n                            },\n                            \"isRaised\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Raised\",\n                                \"default\": true\n                            },\n                            \"isPrimary\": {\n                                \"type\": \"boolean\",\n                                \"title\": \"Primary color\",\n                                \"default\": false\n                            },\n                            \"bgColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"Off state background color\",\n                                \"default\": null\n                            },\n                            \"textColor\": {\n                                \"type\": \"string\",\n                                \"title\": \"Off state text color\",\n                                \"default\": null\n                            }\n                        }, \n                        \"required\": [\n                            \"value\",\n                            \"label\"\n                        ]\n                    }\n                }, \n                \"required\": [\n                    \"on\",\n                    \"off\"\n                ]\n            },\n            \n            \"required\": [\n                \"retrieveMethod\",\n                \"updateMethod\",\n                \"requestTimeout\",\n                \"buttonState\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrieveMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC get value method\"\n                }\n            ]\n        },\n        \"retrieveAttributeKey\",\n        \"retrieveRPCMethod\",\n        {\n            \"key\": \"parseValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\",\n        \n        {\n            \"key\": \"buttonState\",\n            \"items\" :[\n                {\n                    \"key\": \"buttonState.on\",\n                    \"items\": [\n                        \"buttonState.on.value\",\n                        \"buttonState.on.label\",\n                        \"buttonState.on.isRaised\",\n                        \"buttonState.on.isPrimary\",\n                        {\n                            \"key\": \"buttonState.on.bgColor\",\n                            \"type\": \"color\"\n                        }, {\n                            \"key\": \"buttonState.on.textColor\",\n                            \"type\": \"color\"\n                        }\n                    ]\n                }, {\n                    \"key\": \"buttonState.off\",\n                    \"items\": [\n                        \"buttonState.off.value\",\n                        \"buttonState.off.label\",\n                        \"buttonState.off.isRaised\",\n                        \"buttonState.off.isPrimary\",\n                        {\n                            \"key\": \"buttonState.off.bgColor\",\n                            \"type\": \"color\"\n                        }, {\n                            \"key\": \"buttonState.off.textColor\",\n                            \"type\": \"color\"\n                        }\n                    ]\n                }\n            ]\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"buttonState\":{\"on\":{\"value\":\"true\",\"label\":\"On state Label\",\"isRaised\":true,\"isPrimary\":true},\"off\":{\"value\":\"false\",\"label\":\"Off state label\",\"isRaised\":true}}},\"title\":\"Styled button of string value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.segmented_switch_from_string_attribute_send_rpc",
      "name": "Segmented switch of string value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button *ngFor=\"let x of options\" (click)=\"updateValue(x)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n                [color]=\"(x.attributeValue===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"x.attributeValue===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{x?.optionLabel}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.optionStyle = settings.optionStyle;\r\n\r\n    self.ctx.$scope.customCheckedStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.checkedOption.isPrimary === false) {\r\n        self.ctx.$scope.customCheckedStyle['background-color']  = self.ctx.$scope.optionStyle.checkedOption.bgColor;\r\n        self.ctx.$scope.customCheckedStyle['color']             = self.ctx.$scope.optionStyle.checkedOption.textColor;\r\n    }\r\n\r\n    self.ctx.$scope.customUncheckedStyle = {};\r\n    self.ctx.$scope.customUncheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customUncheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === \"column\") {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customUncheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customUncheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.optionStyle.uncheckedOption.isPrimary === false) {\r\n        self.ctx.$scope.customUncheckedStyle['background-color']  = self.ctx.$scope.optionStyle.uncheckedOption.bgColor;\r\n        self.ctx.$scope.customUncheckedStyle['color']             = self.ctx.$scope.optionStyle.uncheckedOption.textColor;\r\n    }\r\n    \r\n    \r\n    //console.log(\"onInit\", settings);\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                if (settings.showError) {\r\n                    self.ctx.$scope.error =\r\n                        rejection.status + \": \" +\r\n                        rejection.statusText;\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    self.ctx.$scope.error = translate.instant('widgets.input-widgets.update-failed');\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        //console.log(\"updateValue()\", settings);\r\n        \r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                option.attributeValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                option.attributeValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                option.attributeValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return data; /* console.log(data); */ \"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value; /* console.log(value); */\"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n\r\n            \"options\":{\r\n                \"type\": \"array\",\r\n                \"title\": \"Options\",\r\n                \"minItems\": 1,\r\n                \"maxItems\": 30,\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"optionLabel\": {\r\n                            \"title\": \"Option label\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"Option label\"\r\n                        },\r\n                        \"attributeValue\": {\r\n                            \"title\": \"Attribute value\",\r\n                            \"type\": \"string\",\r\n                            \"description\": \"Attribute value.\"\r\n                        }\r\n                    }, \r\n                    \"required\": [\r\n                        \"optionLabel\",\r\n                        \"attributeValue\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"retrieveMethod\",\r\n                \"updateMethod\", \r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options[]\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Segmented switch of string value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_time_value",
      "name": "Update time value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 3.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\" style=\"width: 100%; height: 100%;\">\r\n    <form class=\"attribute-update-form\"\r\n          name=\"attrUpdateForm\"\r\n          (ngSubmit)=\"updateValue()\">\r\n          <div style=\"padding: 0 2px; margin: auto 0;\">\r\n            <div class=\"attribute-update-form__grid\" [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\" style=\"height: 26px;\">\r\n                <div class=\"grid__element\" style=\"horizontal-alignment:false; width: 50px;\">\r\n                    <mat-form-field class=\"mat-block\" style=\"width: 100%; height: 26px;\">\r\n\t\t\t\t\t    <input mat-timepicker matInput \r\n                               [(ngModel)]=\"currentValue\"\r\n                               required\r\n                               type=\"time\"\r\n\t\t\t\t\t\t\t   [ngModelOptions]=\"{standalone: true}\" />\r\n                        <mat-error>\r\n                            {{requiredErrorMessage}}\r\n                        </mat-error>\r\n                    </mat-form-field>\r\n                </div>\r\n\r\n                <div class=\"grid__element\" style=\"align-items:center; margin-top: 6px;\">\r\n                    <button mat-icon-button class=\"applyChanges\"\r\n                               type=\"submit\"\r\n                               [disabled]=\"(originalValue === currentValue) || (!currentValue)\"\r\n                               matTooltip=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\r\n                               matTooltipPosition=\"above\">\r\n                        <mat-icon>check</mat-icon>\r\n                    </button>\r\n                    <button mat-icon-button class=\"discardChanges\"\r\n                               type=\"button\"\r\n                               [disabled]=\"originalValue === currentValue\"\r\n                               (click)=\"currentValue = originalValue;\"\r\n                               matTooltip=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\r\n                               matTooltipPosition=\"above\">\r\n                        <mat-icon>close</mat-icon>\r\n                    </button>\r\n                </div>\r\n            </div>\r\n    \r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\" [fxHide]=\"entityDetected\" [innerHtml]=\"message\"></div>\r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                 [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n                {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n            </div>\r\n            <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                 [fxShow]=\"entityDetected && !isValidParameter\">\r\n                {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n            </div>\r\n        </div>\r\n    </form>\r\n</div>\r\n",
        "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-direction: column;\r\n    flex: 1;\r\n}\r\n.grid__element.horizontal-alignment {\r\n    flex-direction: row;\r\n}\r\n.grid__element:last-child {\r\n    align-items: center;\r\n    margin-left: 2px;\r\n}\r\n.grid__element {\r\n    display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n    margin: 0;\r\n    width: 24px;\r\n    min-width: 24px;\r\n    height: 24px;\r\n    min-height: 24px;\r\n    padding: 0 !important;\r\n    margin: 0 !important;\r\n    line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-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.attribute-update-form mdp-date-picker,\r\n.attribute-update-form mdp-time-picker {\r\n    width: 100%;\r\n}\r\n\r\n.attribute-update-form mdp-date-picker md-input-container,\r\n.attribute-update-form mdp-time-picker md-input-container {\r\n    margin: 5px 0 5px;\r\n    width: 100%;\r\n}\r\n\r\n.attribute-update-form grid__element .button.mat-icon-button {\r\n    margin: 2px 0 0;\r\n    padding: 0;\r\n    width: 22px;\r\n    height: 20px;\r\n}\r\n\r\n.attribute-update-form mat-form-field .mat-form-field-infix {\r\n    padding: 0;\r\n    border-top: .6em solid transparent;\r\n}\r\n\r\n.attribute-update-form.small-width mdp-date-picker md-input-container,\r\n.attribute-update-form.small-width mdp-time-picker md-input-container {\r\n    width: 78px;\r\n}\r\n\r\n.show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n",
        "controllerScript": "// v1.0.0, 2023-05-25 13:47\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant            ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n\r\n    ////self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    ////self.ctx.$scope.title = self.ctx.settings.title;\r\n    \r\n    /*function parseValue(data) {\r\n        //Using retrieve data: parse \"09:30\" to \"Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)\"\r\n        let result;\r\n        if (data && data.length>1 && typeof data === \"string\") {\r\n            var strarr = data.split(':', 3);\r\n            if (strarr && strarr.length>=2) {\r\n                var hour = Number(strarr[0]);\r\n                var min = Number(strarr[1]);\r\n                \r\n                if (hour>=0 && hour<=23 && min>=0 && min<=59) {\r\n                    var m = moment(\"1970-01-01 00:00:00.000\")\r\n                    m.hour(hour); \r\n                    m.minute(min); \r\n                    if (m.isValid()) {\r\n                        result = m.toDate();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        console.log(\"parseValue(): \", data, result);\r\n        return result;\r\n    }*/\r\n    let parseValueFunction = (data) => data; //parseValue;  // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) { \r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data; //parseValue;\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    let convertValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n\t\t\t$scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n\t\t\t$scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n\t    $scope.showErrorToast(translate.instant('retrieveRPCMethod is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        var newValue = convertValueFunction($scope.currentValue);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue && newValue.length){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    settings.updateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(settings.updateRPCMethod, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"retrieveAttributeKey\": {\r\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"retrieveRPCMethod\": {\r\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"parseValueFunction\": {\r\n                \"title\": \"Parse value function, f(data), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"updateAttributeKey\": {\r\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"updateRPCMethod\": {\r\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertValueFunction\": {\r\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"retrieveMethod\",\r\n            \"updateMethod\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC set value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"updateAttributeKey\",\r\n        \"updateRPCMethod\",\r\n        {\r\n            \"key\": \"convertValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"requestTimeout\":5000,\"showResultMessage\":true},\"title\":\"Update time value\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[],\"enableDataExport\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_from_flexiable_options",
      "name": "Select double value from flexiable options",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n    \r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttributeKey            = settings.retrieveAttributeKey;\r\n    let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    let lastAttrValue;\r\n    let lastAttrValueOfMin;\r\n    let lastAttrValueOfMax;\r\n    let lastAttrValueOfStep;\r\n    let lastAttrValueOfUnit;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue;\r\n        let attrValueOfMin;\r\n        let attrValueOfMax;\r\n        let attrValueOfStep;\r\n        let attrValueOfUnit;\r\n        \r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttributeKey && keyName === retrieveAttributeKey) {\r\n                    /*try { \r\n                        attrValue = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttributeKeyOfMinValue && keyName === retrieveAttributeKeyOfMinValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfMin = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfMaxValue && keyName === retrieveAttributeKeyOfMaxValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfMax = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfStepValue && keyName === retrieveAttributeKeyOfStepValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfStep = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfUnit && keyName === retrieveAttributeKeyOfUnit) {\r\n                    attrValueOfUnit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValueOfMin, attrValueOfMax, attrValueOfStep, attrValueOfUnit, attrValue);\r\n        //console.log(lastAttrValueOfMin, lastAttrValueOfMax, lastAttrValueOfStep, lastAttrValueOfUnit);\r\n        if (attrValueOfMin !== lastAttrValueOfMin || attrValueOfMax !== lastAttrValueOfMax ||\r\n            attrValueOfStep !== lastAttrValueOfStep || attrValueOfUnit !== lastAttrValueOfUnit) {\r\n\r\n            if (attrValueOfMin)  lastAttrValueOfMin = attrValueOfMin;\r\n            if (attrValueOfMax)  lastAttrValueOfMax = attrValueOfMax;\r\n            if (attrValueOfStep) lastAttrValueOfStep = attrValueOfStep; \r\n            if (attrValueOfUnit) lastAttrValueOfUnit = attrValueOfUnit;\r\n            \r\n            if (attrValueOfMin && attrValueOfMax && attrValueOfStep) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValueOfMin;\r\n                let fMax = factor*attrValueOfMax;\r\n                let fStep = factor*attrValueOfStep;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValueOfMin, i=0; val<factor*attrValueOfMax+factor*attrValueOfStep; i++, val=factor*attrValueOfMin+factor*attrValueOfStep*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValueOfUnit && attrValueOfUnit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValueOfUnit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttributeKey && retrieveAttributeKey.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKey } );\r\n            }\r\n            if (retrieveAttributeKeyOfMinValue && retrieveAttributeKeyOfMinValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfMinValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfMaxValue && retrieveAttributeKeyOfMaxValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfMaxValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfStepValue && retrieveAttributeKeyOfStepValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfStepValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfUnit && retrieveAttributeKeyOfUnit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfUnit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n\r\n\r\n/*\"retrieveMethod\": {\r\n    \"title\": \"Retrieve double value using method\",\r\n    \"type\": \"string\",\r\n    \"default\": \"rpc\"\r\n},\r\n\"retrieveAttributeKey\": {\r\n    \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n    \"type\": \"string\",\r\n    \"default\": \"\"\r\n},\r\n\"retrieveRPCMethod\": {\r\n    \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n    \"type\": \"string\",\r\n    \"default\": \"\"\r\n},\r\n\"parseValueFunction\": {\r\n    \"title\": \"Parse value function, f(data), returns double value\",\r\n    \"type\": \"string\",\r\n    \"default\": \"return data; \"\r\n},\r\n----------------------------------------------------------\r\n{\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n\r\n*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveAttributeKey\": {\n                \"title\": \"Device attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfMinValue\": {\n                \"title\": \"Device attribute key of Min value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfMaxValue\": {\n                \"title\": \"Device attribute key of Max value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfStepValue\": {\n                \"title\": \"Device attribute key of Step value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfUnit\": {\n                \"title\": \"Device attribute key of Unit\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n\n            \"required\": [\n                \"retrieveAttributeKey\",\n                \"retrieveAttributeKeyOfMinValue\",\n                \"retrieveAttributeKeyOfMaxValue\",\n                \"retrieveAttributeKeyOfStepValue\",\n                \"updateMethod\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        \"retrieveAttributeKey\",\n        \"retrieveAttributeKeyOfMinValue\",\n        \"retrieveAttributeKeyOfMaxValue\",\n        \"retrieveAttributeKeyOfStepValue\",\n        \"retrieveAttributeKeyOfUnit\",\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Select double value from flexiable options\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_time_value_with_pattern_key",
      "name": "Update time value with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 6.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\"\r\n    style=\"width: 100%; height: 100%;\">\r\n\r\n    <div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n        <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n            class=\"title-container\" fxLayout=\"row\"\r\n            fxLayoutAlign=\"start center\"\r\n            [fxShow]=\"showTitle\">\r\n            <span class=\"button-title\">{{title}}</span>\r\n        </div>\r\n\r\n        <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n            class=\"button-container\" fxLayout=\"column\"\r\n            fxLayoutAlign=\"center center\">\r\n\r\n            <form class=\"attribute-update-form\"\r\n                name=\"attrUpdateForm\"\r\n                (ngSubmit)=\"updateValue()\"\r\n                [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\">\r\n\r\n                <mat-form-field class=\"mat-block\" >\r\n                    <input mat-timepicker matInput\r\n                        [(ngModel)]=\"currentValue\" required\r\n                        type=\"time\"\r\n                        [ngModelOptions]=\"{standalone: true}\" />\r\n                </mat-form-field>\r\n\r\n                <button mat-icon-button\r\n                    class=\"applyChanges\" type=\"submit\"\r\n                    [disabled]=\"(originalValue === currentValue) || (!currentValue)\"\r\n                    matTooltip=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\r\n                    matTooltipPosition=\"above\">\r\n                    <mat-icon>check</mat-icon>\r\n                </button>\r\n                <button mat-icon-button\r\n                    class=\"discardChanges\" type=\"button\"\r\n                    [disabled]=\"originalValue === currentValue\"\r\n                    (click)=\"currentValue = originalValue;\"\r\n                    matTooltip=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\r\n                    matTooltipPosition=\"above\">\r\n                    <mat-icon>close</mat-icon>\r\n                </button>\r\n\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxHide]=\"entityDetected\"\r\n                    [innerHtml]=\"message\"></div>\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n                    {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n                </div>\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxShow]=\"entityDetected && !isValidParameter\">\r\n                    {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n                </div>\r\n\r\n            </form>\r\n            \r\n            <!--<mat-error>-->\r\n            <!--    {{requiredErrorMessage}}-->\r\n            <!--</mat-error>-->\r\n\r\n        </div>\r\n    </div>\r\n\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n/*.tb-rpc-button .button-container div{*/\r\n/*    min-width: 80%;*/\r\n/*}*/\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container input {\r\n    /*font-size: 1em;*/\r\n    font-weight: bold;\r\n    /*line-height: 1.5;*/\r\n    /*color: #333333;*/\r\n}\r\n\r\n/*.tb-rpc-button .button-container .mat-button{*/\r\n/*    width: 100%;*/\r\n/*    margin: 0;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .button-container .mat-button{*/\r\n/*    padding: 0 0px;*/\r\n/*}*/\r\n\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.tb-rpc-button .attribute-update-form {\r\n    /*overflow: hidden;*/\r\n    /*width: 100%;*/\r\n    /*height: 100%;*/\r\n    display: flex;\r\n    /*flex-direction: row;*/\r\n    /*justify-content: center;*/\r\n    /*align-items: center;*/\r\n}\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-button.mat-icon-button {*/\r\n/*    margin: 0;*/\r\n/*    width: 24px;*/\r\n/*    min-width: 24px;*/\r\n/*    height: 24px;*/\r\n/*    min-height: 24px;*/\r\n/*    padding: 0 !important;*/\r\n/*    margin: 0 !important;*/\r\n/*    line-height: 20px;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-icon-button mat-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/*.tb-rpc-button .attribute-update-form mdp-date-picker,*/\r\n/*.tb-rpc-button .attribute-update-form mdp-time-picker {*/\r\n/*    width: 100%;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form mdp-date-picker md-input-container,*/\r\n/*.tb-rpc-button .attribute-update-form mdp-time-picker md-input-container */\r\n/*{*/\r\n/*    margin: 5px 0 5px;*/\r\n/*    width: 100%;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form grid__element .button.mat-icon-button*/\r\n/*{*/\r\n/*    margin: 2px 0 0;*/\r\n/*    padding: 0;*/\r\n/*    width: 22px;*/\r\n/*    height: 20px;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form mat-form-field .mat-form-field-infix */\r\n/*{*/\r\n/*    padding: 0;*/\r\n/*    border-top: .6em solid transparent;*/\r\n/*    width: fit-content;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form.small-width mdp-date-picker md-input-container,*/\r\n/*.tb-rpc-button .attribute-update-form.small-width mdp-time-picker md-input-container {*/\r\n/*    width: 78px;*/\r\n/*}*/\r\n\r\n.tb-rpc-button .show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n\r\n/*.tb-rpc-button .attribute-update-form  .mat-mdc-icon-button.mat-mdc-button-base {*/\r\n/*    width: 36px;*/\r\n/*    height: 36px;*/\r\n/*    padding: 8px;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form .mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea) .mat-mdc-form-field-infix {*/\r\n/*    padding-top: 8px;*/\r\n/*    padding-bottom: 8px;*/\r\n/*}*/\r\n\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-mdc-form-field {*/\r\n/*    flex-direction: row;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-mdc-form-field-subscript-wrapper {*/\r\n/*    width: 0px;*/\r\n/*    height: 0px;*/\r\n/*}*/\r\n\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-hint-spacer {\r\n    flex: 0 0 0;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-hint-wrapper,\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-error-wrapper {\r\n    /*position: absolute;*/\r\n    /*top: 0;*/\r\n    /*left: 0;*/\r\n    /*right: 0;*/\r\n    width: 0;\r\n    padding: 0 0;\r\n}\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-mdc-form-field-subscript-wrapper {*/\r\n/*    width: 0px;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form .mat-mdc-form-field-flex {*/\r\n/*    width: fit-content;*/\r\n/*}*/\r\n\r\n/*.tb-rpc-button .attribute-update-form .mdc-text-field__input {*/\r\n/*    width: fit-content;*/\r\n/*}*/\r\n\r\n.tb-rpc-button .attribute-update-form .mdc-text-field{\r\n    /*padding: 0 4px;*/\r\n    padding: 0 4px;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-subscript-wrapper,\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-bottom-align:before {\r\n    /*-moz-osx-font-smoothing: grayscale;*/\r\n    /*-webkit-font-smoothing: antialiased;*/\r\n    /*font-family: var(--mdc-typography-caption-font-family,var(--mdc-typography-font-family,Roboto,sans-serif));*/\r\n    /*font-size: var(--mdc-typography-caption-font-size,12px);*/\r\n    /*line-height: var(--mdc-typography-caption-line-height,20px);*/\r\n    line-height: 0px;\r\n    height: 0px;\r\n    /*font-weight: var(--mdc-typography-caption-font-weight,400);*/\r\n    /*letter-spacing: var(--mdc-typography-caption-letter-spacing,.0333333333em);*/\r\n    /*-webkit-text-decoration: var(--mdc-typography-caption-text-decoration,inherit);*/\r\n    /*text-decoration: var(--mdc-typography-caption-text-decoration,inherit);*/\r\n    /*text-transform: var(--mdc-typography-caption-text-transform,none)*/\r\n}\r\n\r\n\r\n.tb-rpc-button .attribute-update-form .mat-mdc-form-field-infix {\r\n    /*width: 180px;*/\r\n    /*min-height: 56px;*/\r\n    min-height: 48px;\r\n    width: 100px;\r\n}\r\n\r\n.tb-rpc-button .mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea) .mat-mdc-form-field-infix {\r\n    /*padding-top: 16px;*/\r\n    /*padding-bottom: 16px;*/\r\n    padding-top: 12px;\r\n    padding-bottom: 12px;\r\n}",
        "controllerScript": "// v.2.4, 2023-09-25 17:00\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    // $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant            ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n\r\n    self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    self.ctx.$scope.title = self.ctx.settings.title;\r\n    \r\n    if (self.ctx.isEdit) {\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"10:10\";\r\n    }\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n\r\n    /*function parseValue(data) {\r\n        //Using retrieve data: parse \"09:30\" to \"Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)\"\r\n        let result;\r\n        if (data && data.length>1 && typeof data === \"string\") {\r\n            var strarr = data.split(':', 3);\r\n            if (strarr && strarr.length>=2) {\r\n                var hour = Number(strarr[0]);\r\n                var min = Number(strarr[1]);\r\n                \r\n                if (hour>=0 && hour<=23 && min>=0 && min<=59) {\r\n                    var m = moment(\"1970-01-01 00:00:00.000\")\r\n                    m.hour(hour); \r\n                    m.minute(min); \r\n                    if (m.isValid()) {\r\n                        result = m.toDate();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        console.log(\"parseValue(): \", data, result);\r\n        return result;\r\n    }*/\r\n\r\n    let patternParamOfetrievedAttributeKeyOrMethodName = \" \";\r\n    if (settings.retrieveMethod === \"attribute\" || settings.retrieveMethod === \"SHARED_SCOPE\" || settings.retrieveMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveAttributeKey;\r\n    } else if (settings.retrieveMethod === \"timeseries\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveTimeseriesKey;\r\n    } else if (settings.retrieveMethod === \"rpc\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveRPCMethodName;\r\n    }\r\n\r\n    let convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertRetrievedAttributeKeyOrMethodNameFunction && settings.convertRetrievedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfetrievedAttributeKeyOrMethodName', 'patternCommonParam', settings.convertRetrievedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let retrievedAttributeKeyOrMethodName = convertRetrievedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertRetrievedValueFunction = (value) => value; //parseValue;  // Retrieve data\r\n    if (settings.convertRetrievedValueFunction && settings.convertRetrievedValueFunction.length) { \r\n        try {\r\n            convertRetrievedValueFunction = new Function('value', settings.convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = (value) => value; //parseValue;\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    \r\n    let patternParamOfUpdateAttributeKeyOrMethodName = \" \";\r\n    if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateAttributeKey;\r\n    } else if (settings.updateMethod === \"timeseries\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateTimeseriesKey;\r\n    } else if (settings.updateMethod === \"rpc\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    }\r\n\r\n    let convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertUpdatedAttributeKeyOrMethodNameFunction && settings.convertUpdatedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfUpdateAttributeKeyOrMethodName', 'patternCommonParam', settings.convertUpdatedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let updateAttributeKeyOrMethodName = convertUpdatedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam);\r\n    \r\n    let convertUpdatedValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertUpdatedValueFunction && settings.convertUpdatedValueFunction.length) {\r\n        try {\r\n            convertUpdatedValueFunction = new Function('value', settings.convertUpdatedValueFunction);\r\n        } catch (e) {\r\n            convertUpdatedValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    \r\n    /*function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }*/\r\n    \r\n    function subscribeAttributes(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [\r\n        //        {name: key}\r\n        //    ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    function subscribeTimeseries(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [\r\n        //        {name: key}\r\n        //    ];\r\n        //} else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrievedAttributeKeyOrMethodName is null.\";\r\n            $scope.showErrorToast(translate.instant('retrievedAttributeKeyOrMethodName is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        var newValue = convertUpdatedValueFunction($scope.currentValue);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue && newValue.length){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateTimeseriesKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateRPCMethodName, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(retrievedAttributeKeyOrMethodName, settings.requestTimeout); //settings.patternParamOfRetrieveRPCMethodName\r\n                } else if (settings.retrieveMethod == 'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveAttributeKey\r\n                    }\r\n                } else if (settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveTimeseriesKey\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"patternCommonParam\": {\r\n                \"title\": \"Pattern common param\",\r\n                \"type\": \"string\",\r\n                \"default\": \"0\"\r\n            },\r\n\r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfRetrieveAttributeKey\": {\r\n                \"title\": \"Pattern param of retrieved Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveTimeseriesKey\": {\r\n                \"title\": \"Pattern param of retrieved Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveRPCMethodName\": {\r\n                \"title\": \"Pattern param of retrieved value using server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n                \"convertRetrievedAttributeKeyOrMethodNameFunction\": {\r\n                    \"title\": \"Convert function, f(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam), returns retireved attribute key, time-series data key or server-side RPC method name\",\r\n                    \"type\": \"string\",\r\n                    \"default\": \"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n                },\r\n            \"convertRetrievedValueFunction\": {\r\n                \"title\": \"Parse value function, f(value), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfUpdateAttributeKey\": {\r\n                \"title\": \"Pattern param of updated Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateTimeseriesKey\": {\r\n                \"title\": \"Pattern param of updated Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateRPCMethodName\": {\r\n                \"title\": \"Pattern param of updated server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n                \"convertUpdatedAttributeKeyOrMethodNameFunction\": {\r\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam), returns updated attribute key, time-series data key or server-side RPC method name\",\r\n                    \"type\": \"string\",\r\n                    \"default\": \"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n                },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"convertUpdatedValueFunction\": {\r\n                \"title\": \"Convert function, f(value), returns updated value\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"patternCommonParam\",\r\n            \"retrieveMethod\",\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"updateMethod\",\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"patternCommonParam\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-dide RPC to get value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveAttributeKey\",\r\n            \"condition\": \"model.retrieveMethod === 'attribute' || model.retrieveMethod === 'SHARED_SCOPE' || model.retrieveMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveTimeseriesKey\",\r\n            \"condition\": \"model.retrieveMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveRPCMethodName\",\r\n            \"condition\": \"model.retrieveMethod === 'rpc'\"\r\n        },\r\n            {\r\n                \"key\": \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n                \"type\": \"javascript\"\r\n            },\r\n        {\r\n            \"key\": \"convertRetrievedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-side RPC to set value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateAttributeKey\",\r\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\r\n            \"condition\": \"model.updateMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\r\n            \"condition\": \"model.updateMethod === 'rpc'\"\r\n        },\r\n            {\r\n                \"key\": \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n                \"type\": \"javascript\"\r\n            },\r\n        {\r\n            \"key\": \"convertUpdatedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"updateMethod\":\"rpc\",\"requestTimeout\":5000,\"showResultMessage\":true,\"patternCommonParam\":\"0\",\"convertRetrievedAttributeKeyOrMethodNameFunction\":\"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\",\"convertRetrievedValueFunction\":\"return value;\",\"convertUpdatedAttributeKeyOrMethodNameFunction\":\"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\",\"convertUpdatedValueFunction\":\"return value;\",\"title\":\"Update time value with pattern key\"},\"title\":\"Update time value with pattern key\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[],\"enableDataExport\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_with_state_parameter_from_flexiable_option",
      "name": "Select double value from flexiable option with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 4,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n    let patternParamOfRetrievedAttributeKey = settings.retrieveAttributes.patternParamOfRetrievedAttributeKey;\r\n    let retrievedAttributeKeyConvertFunc = (stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam) => patternParamOfRetrievedAttributeKey + patternCommonParam;\r\n    if (settings.retrieveAttributes.retrievedAttributeKeyConvertFunc && settings.retrieveAttributes.retrievedAttributeKeyConvertFunc.length) {\r\n        try {\r\n            retrievedAttributeKeyConvertFunc = new Function('stateParams', 'patternParamOfRetrievedAttributeKey', 'patternCommonParam', settings.retrieveAttributes.retrievedAttributeKeyConvertFunc);\r\n        } catch (e) {\r\n            retrievedAttributeKeyConvertFunc = (stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam) => patternParamOfRetrievedAttributeKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateAttributeKey = settings.patternParamOfUpdateAttributeKey;\r\n    let updateAttributeKeyConvertFunc = (stateParams, patternParamOfUpdateAttributeKey, patternCommonParam) => patternParamOfUpdateAttributeKey + patternCommonParam;\r\n    if (settings.updateAttributeKeyConvertFunc && settings.updateAttributeKeyConvertFunc.length) {\r\n        try {\r\n            updateAttributeKeyConvertFunc = new Function('stateParams', 'patternParamOfUpdateAttributeKey', 'patternCommonParam', settings.updateAttributeKeyConvertFunc);\r\n        } catch (e) {\r\n            updateAttributeKeyConvertFunc = (stateParams, patternParamOfUpdateAttributeKey, patternCommonParam) => patternParamOfUpdateAttributeKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateTimeseriesKey = settings.patternParamOfUpdateTimeseriesKey;\r\n    let updateTimeseriesKeyConvertFunc = (stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam) => patternParamOfUpdateTimeseriesKey + patternCommonParam;\r\n    if (settings.updateTimeseriesKeyConvertFunc && settings.updateTimeseriesKeyConvertFunc.length) {\r\n        try {\r\n            updateTimeseriesKeyConvertFunc = new Function('stateParams', 'patternParamOfUpdateTimeseriesKey', 'patternCommonParam', settings.updateTimeseriesKeyConvertFunc);\r\n        } catch (e) {\r\n            updateTimeseriesKeyConvertFunc = (stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam) => patternParamOfUpdateTimeseriesKey + patternCommonParam;\r\n        }\r\n    }\r\n    let patternParamOfUpdateRPCMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    let updateRPCMethodConvertNameFunc = (stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam) => patternParamOfUpdateRPCMethodName + patternCommonParam;\r\n    if (settings.updateRPCMethodConvertNameFunc && settings.updateRPCMethodConvertNameFunc.length) {\r\n        try {\r\n            updateRPCMethodConvertNameFunc = new Function('stateParams', 'patternParamOfUpdateRPCMethodName', 'patternCommonParam', settings.updateRPCMethodConvertNameFunc);\r\n        } catch (e) {\r\n            updateRPCMethodConvertNameFunc = (stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam) => patternParamOfUpdateRPCMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    /* //////////////////////////////////////////////////////////////////////////////////////////////////////////\r\n\tretrieveAttributes_adjusted = retrievedAttributeKeyConvertFunc(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam);\r\n\tupdateAttributeKey = updateAttributeKeyConvertFunc(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam);\r\n\tupdateTimeseriesKey = updateTimeseriesKeyConvertFunc(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam);\r\n\tupdateRPCMethod = updateRPCMethodConvertNameFunc(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam);\r\n\t///////////////////////////////////////////////////////////////////////////////////////////////////////////// */\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttrKey = {};\r\n    retrieveAttrKey.retrieved = retrievedAttributeKeyConvertFunc(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam); //settings.retrieveAttributes.retrieved; //let retrieveAttributeKey = settings.retrieveAttributeKey;\r\n    retrieveAttrKey.min      = settings.retrieveAttributes.min;     //let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    retrieveAttrKey.max      = settings.retrieveAttributes.max;     //let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    retrieveAttrKey.step     = settings.retrieveAttributes.step;    //let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    retrieveAttrKey.unit     = settings.retrieveAttributes.unit;    //let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    \r\n    \r\n    //console.log(\"retrievedAttributeKeyConvertFunc\", retrievedAttributeKeyConvertFunc);\r\n    //console.log(\"stateParams\", stateParams);\r\n    //console.log(\"patternParamOfRetrievedAttributeKey\", patternParamOfRetrievedAttributeKey);\r\n    //console.log(\"patternCommonParam\", patternCommonParam);\r\n    //console.log(\"retrieveAttrKey.retrieved\", retrieveAttrKey.retrieved);\r\n    //console.log(\"settings\", settings);\r\n    \r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue = {};\r\n\r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttrKey.retrieved && keyName === retrieveAttrKey.retrieved) {\r\n                    /*try { \r\n                        attrValue.updated = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.updated = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttrKey.min && keyName === retrieveAttrKey.min) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.min = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.max && keyName === retrieveAttrKey.max) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.max = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.step && keyName === retrieveAttrKey.step) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.step = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.unit && keyName === retrieveAttrKey.unit) {\r\n                    attrValue.unit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValue.min, attrValue.max, attrValue.step, attrValue.unit, attrValue.updated);\r\n        //console.log(lastAttrValue.min, lastAttrValue.max, lastAttrValue.step, lastAttrValue.unit);\r\n        if (attrValue.min !== lastAttrValue.min || attrValue.max !== lastAttrValue.max ||\r\n            attrValue.step !== lastAttrValue.step || attrValue.unit !== lastAttrValue.unit) {\r\n\r\n            if (attrValue.min)  lastAttrValue.min = attrValue.min;\r\n            if (attrValue.max)  lastAttrValue.max = attrValue.max;\r\n            if (attrValue.step) lastAttrValue.step = attrValue.step; \r\n            if (attrValue.unit) lastAttrValue.unit = attrValue.unit;\r\n            \r\n            if (attrValue.min && attrValue.max && attrValue.step) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValue.min;\r\n                let fMax = factor*attrValue.max;\r\n                let fStep = factor*attrValue.step;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValue.min, i=0; val<factor*attrValue.max+factor*attrValue.step; i++, val=factor*attrValue.min+factor*attrValue.step*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValue.unit && attrValue.unit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValue.unit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue.updated) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue.updated);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue.updated, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttrKey.retrieved && retrieveAttrKey.retrieved.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.retrieved } );\r\n            }\r\n            if (retrieveAttrKey.min && retrieveAttrKey.min.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.min } );\r\n            }\r\n            if (retrieveAttrKey.max && retrieveAttrKey.max.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.max } );\r\n            }\r\n            if (retrieveAttrKey.step && retrieveAttrKey.step.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.step } );\r\n            }\r\n            if (retrieveAttrKey.unit && retrieveAttrKey.unit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.unit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        //console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                updateAttributeKeyConvertFunc(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam), //settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                updateTimeseriesKeyConvertFunc(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam), //settings.updateTimeseriesKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(updateRPCMethodConvertNameFunc(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam), //settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"patternCommonParam\": {\n                \"title\": \"Pattern common param\",\n                \"type\": \"string\",\n                \"default\": \"0\"\n            },\n\n            \"retrieveAttributes\": {\n                \"type\": \"object\",\n                \"title\": \"Retrieve device attributes\",\n                \"properties\": {\n                    \"patternParamOfRetrievedAttributeKey\": {\n                        \"title\": \"Pattern param of retrieved device attribute key\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                        \"retrievedAttributeKeyConvertFunc\": {\n                            \"title\": \"Convert function, f(stateParams, patternParamOfRetrievedAttributeKey, patternCommonParam), returns retireved device attribute key\",\n                            \"type\": \"string\",\n                            \"default\": \"return patternParamOfRetrievedAttributeKey + patternCommonParam; /* console.log(value); */\"\n                        },\n                    \"min\": {\n                        \"title\": \"Min value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"max\": {\n                        \"title\": \"Max value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"step\": {\n                        \"title\": \"Step value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"unit\": {\n                        \"title\": \"Unit of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    }\n                },\n                \"required\": [\n                    \"patternParamOfRetrievedAttributeKey\",\n                        \"retrievedAttributeKeyConvertFunc\",\n                    \"min\",\n                    \"max\",\n                    \"step\",\n                    \"unit\"\n                ]\n            },\n\n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"patternParamOfUpdateAttributeKey\": {\n                \"title\": \"Pattern param of updated attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateAttributeKeyConvertFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKey, patternCommonParam), returns updated attribute key\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateAttributeKey + patternCommonParam; /* console.log(value); */\"\n                },\n            \"patternParamOfUpdateTimeseriesKey\": {\n                \"title\": \"Pattern param of updated time-series data key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateTimeseriesKeyConvertFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateTimeseriesKey, patternCommonParam), returns updated time-series data key\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateTimeseriesKey + patternCommonParam; /* console.log(value); */\"\n                },\n            \"patternParamOfUpdateRPCMethodName\": {\n                \"title\": \"Pattern param of updated server-side RPC method name\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n                \"updateRPCMethodConvertNameFunc\": {\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateRPCMethodName, patternCommonParam), returns using server-side RPC method name\",\n                    \"type\": \"string\",\n                    \"default\": \"return patternParamOfUpdateRPCMethodName + patternCommonParam; /* console.log(value); */\"\n                },\n            \n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert function of updated value, f(value), returns updated value\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"patternCommonParam\",\n            \"retrieveAttributes\",\n            \"updateMethod\",\n            \"convertValueFunction\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        \"patternCommonParam\",\n\n        \"retrieveAttributes.patternParamOfRetrievedAttributeKey\",\n            {\n                \"key\": \"retrieveAttributes.retrievedAttributeKeyConvertFunc\",\n                \"type\": \"javascript\"\n            },\n        \"retrieveAttributes.min\",\n        \"retrieveAttributes.max\",\n        \"retrieveAttributes.step\",\n        \"retrieveAttributes.unit\",\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update time-series data\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        {\n            \"key\": \"patternParamOfUpdateAttributeKey\",\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\n        },\n            {\n                \"key\": \"updateAttributeKeyConvertFunc\",\n                \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\n            \"condition\": \"model.updateMethod === 'timeseries'\"\n        },\n            {\n                \"key\": \"updateTimeseriesKeyConvertFunc\",\n                \"condition\": \"model.updateMethod === 'timeseries'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n            {\n                \"key\": \"updateRPCMethodConvertNameFunc\",\n                \"condition\": \"model.updateMethod === 'rpc'\",\n                \"type\": \"javascript\"\n            },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"retrieveAttributes\":{\"adjusted\":\"adjust\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethod\":\"setValue\"},\"title\":\"Select double value from flexiable option with pattern key\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.setting_list",
      "name": "Setting list",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 9,
        "sizeY": 3,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? '1.1em' : '0em'}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '5px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"start center\">\r\n\r\n        <mat-selection-list #shoes [multiple]=\"false\"\r\n            (selectionChange)=\"listSelectionChange(shoes.selectedOptions.selected[0].value)\"\r\n            id=\"list_of_days_of_weeks\">\r\n            <mat-list-option\r\n                *ngFor=\"let item of settingList; index as index; last as last\"\r\n                [value]=\"index\"\r\n                [disabled]=\"isDsiabledListOption(index)\">\r\n                <div mat-line>\r\n                    <mat-icon mat-list-icon matListItemIcon>{{item.icon}}\r\n                    </mat-icon> &nbsp;\r\n                    <div>{{item.text}}</div>\r\n                    <div> > </div>\r\n                </div>\r\n                <mat-divider [inset]=\"true\" *ngIf=\"!last\">\r\n                </mat-divider>\r\n            </mat-list-option>\r\n        </mat-selection-list>\r\n\r\n    </div>\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container mat-action-list{\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.6;\r\n    color: #333333;\r\n    \r\n    width: 100%;\r\n}\r\n\r\n/* TC 20230523 */\r\n.tb-rpc-button .button-container mat-selection-list{\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.6;\r\n    color: #333333;\r\n    \r\n    width: 100%;\r\n}\r\n\r\n/* TC 20230523 */\r\n.tb-rpc-button .mdc-list-item__end.ng-star-inserted {\r\n    visibility: hidden;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-list-base .mat-list-item.mat-list-item-with-avatar, .mat-list-base .mat-list-option.mat-list-item-with-avatar {\r\n    height: 2.4em;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-line\r\n{\r\n    display: flex;\r\n    flex-direction: row;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// v.1.0.0, 2023-05-25 17:24\r\n\r\n// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n        init();\r\n        self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(\r\n        self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils\r\n        .guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings\r\n        .title.length ? true :\r\n        false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.convertedValue = 0;\r\n\r\n    // let stateParams = {};\r\n    // if (!self.ctx.isEdit && self.ctx.stateController) {\r\n    //     stateParams = self.ctx.stateController\r\n    //         .getStateParams();\r\n    // }\r\n    let widgetContext = self.ctx;\r\n\r\n    let convertRetrievedValueFunction = (retrievedValue) =>\r\n        retrievedValue; // Retrieved data\r\n    if (settings.convertRetrievedValueFunction && settings\r\n        .convertRetrievedValueFunction.length) {\r\n        try {\r\n            convertRetrievedValueFunction = new Function(\r\n                'retrievedValue', settings\r\n                .convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = (\r\n                retrievedValue) => retrievedValue;\r\n        }\r\n    }\r\n\r\n    //self.ctx.$scope.settingList = settings.settingList;\r\n    self.ctx.$scope.settingList = [];\r\n    for (const item of settings.settingList) {\r\n        self.ctx.$scope.settingList.push({\r\n            \"icon\": item.icon,\r\n            \"text\": item.text,\r\n            \"isDisabledFunc\": new Function(\r\n                'convertedValue', item\r\n                .isDisabledFunc),\r\n            \"customActionFunc\": new Function(\r\n                'convertedValue', 'widgetContext',\r\n                'openRightLayout', item\r\n                .customActionFunc)\r\n        });\r\n    }\r\n\r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription,\r\n        apply) {\r\n        let convertedValue;\r\n\r\n        for (let i = 0; subscription.data && i <\r\n            subscription.data.length; i++) {\r\n            let keyName = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription\r\n                .data[i].data.length > 0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                //if (settings.retrievedAttributeKey && keyName === settings.retrievedAttributeKey) {\r\n                    try {\r\n                        convertedValue = convertRetrievedValueFunction(KeyValue); ////angular.fromJson(newValue)\r\n                    } catch (e) {\r\n                        self.ctx.$scope.error =\r\n                            'convertRetrievedValueFunction(value) error!'\r\n                    }\r\n                    //if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                    //    convertedValue = KeyValue;\r\n                    //}\r\n                //} else {\r\n                //    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                //    console.log(subscription.data[i]);\r\n                //}\r\n            }\r\n        }\r\n\r\n        if (convertedValue) {\r\n            self.ctx.$scope.convertedValue =\r\n                convertedValue; //String(convertedValue);\r\n            //refreshTextColor();\r\n        }\r\n\r\n        if (apply) {\r\n            self.ctx\r\n                .detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n\r\n    function onDataUpdateErrorForSubscribe(subscription,\r\n        e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\r\n            \"onDataUpdateErrorForSubscribe(): errorText=\",\r\n            errorText);\r\n    }\r\n\r\n    function subscribeAttributes(type, entityType, entityId,\r\n        key) { //retrievedMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type: type,\r\n            entityType: entityType,\r\n            entityId: entityId\r\n        }];\r\n\r\n        //if (retrievedMethod == 'attribute') {\r\n        valueSubscriptionInfo[0].attributes = [{\r\n            name: key\r\n        }];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        //}\r\n\r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo(\r\n                'latest', valueSubscriptionInfo,\r\n                subscriptionOptions, false, true)\r\n            .subscribe( //types.widgetType.latest.value\r\n                (subscription) => {\r\n                    valueSubscription = subscription;\r\n                }\r\n            );\r\n    }\r\n\r\n    function subscribeTimeseries(type, entityType, entityId,\r\n        key) { //retrievedMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type: type,\r\n            entityType: entityType,\r\n            entityId: entityId\r\n        }];\r\n\r\n        //if (retrievedMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [{name: key}];\r\n        //} else {\r\n        valueSubscriptionInfo[0].timeseries = [{\r\n            name: key\r\n        }];\r\n        //}\r\n\r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo(\r\n                'latest', valueSubscriptionInfo,\r\n                subscriptionOptions, false, true)\r\n            .subscribe( //types.widgetType.latest.value\r\n                (subscription) => {\r\n                    valueSubscription = subscription;\r\n                }\r\n            );\r\n    }\r\n\r\n\r\n    // RPC retrieved value\r\n    function rpcRetrievedValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method,\r\n                null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.convertedValue = responseBody;\r\n                    self.ctx.$scope.convertedValue =\r\n                        convertRetrievedValueFunction(\r\n                            responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx\r\n                        .defaultSubscription\r\n                        .rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error =\r\n                \"retrievedRPCMethodName is null.\";\r\n        }\r\n    }\r\n\r\n    self.ctx.$scope.isDsiabledListOption = function(index) {\r\n        //console.log(\"isDsiabledListOption()\", index);\r\n        //if (self.ctx.$scope.settingList[index].isDisabledFunc) {\r\n        return self.ctx.$scope.settingList[index]\r\n            .isDisabledFunc(self.ctx.$scope\r\n                .convertedValue);\r\n        //}\r\n    }\r\n    self.ctx.$scope.listSelectionChange = function(index) {\r\n        //console.log(\"listSelectionChange()\", index);\r\n        self.ctx.$scope.settingList[index]\r\n            .customActionFunc(self.ctx.$scope\r\n                .convertedValue, widgetContext, false);\r\n    }\r\n\r\n    // retrieved value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription\r\n            .rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error =\r\n                'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrievedMethod == 'rpc') {\r\n                    rpcRetrievedValue(settings\r\n                        .retrievedRPCMethodName,\r\n                        settings.requestTimeout);\r\n                } else if (settings.retrievedMethod ==\r\n                    'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\r\n                            \"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription\r\n                            .targetDeviceId,\r\n                            //settings.retrievedMethod, \r\n                            settings\r\n                            .retrievedAttributeKey);\r\n                    }\r\n                } else if (settings.retrievedMethod ==\r\n                    'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\r\n                            \"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription\r\n                            .targetDeviceId,\r\n                            //settings.retrievedMethod, \r\n                            settings\r\n                            .retrievedTimeseriesKey);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        firstRetrieveValue = false;\r\n    }\r\n\r\n    retrieveValue();\r\n}\r\n\r\nself.onResize = function() {};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(\r\n            valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.convertedValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"retrievedMethod\": {\n                \"title\": \"Retrieve value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrievedAttributeKey\": {\n                \"title\": \"Retrieved Attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrievedTimeseriesKey\": {\n                \"title\": \"Retrieved Timeseries key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrievedRPCMethodName\": {\n                \"title\": \"Retrieved value using server-side RPC method name\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertRetrievedValueFunction\": {\n                \"title\": \"Function body to parse retrieved value, f(retrievedValue)\",\n                \"type\": \"string\",\n                \"default\": \"return retrievedValue; //return true;\"\n            },\n\n            \"settingList\": {\n                \"type\": \"array\",\n                \"title\": \"Setting items\",\n                \"minItems\": 1,\n                \"maxItems\": 30,\n                \"items\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"icon\": {\n                            \"title\": \"Material icon, e.g., ac_unit, timer, settings and build.\",\n                            \"type\": \"string\",\n                            \"default\": \"settings\"\n                        },\n                        \"text\": {\n                            \"title\": \"Text\",\n                            \"type\": \"string\",\n                            \"default\": \"\"\n                        },\n                        \"isDisabledFunc\": {\n                            \"title\": \"Method body to detect if it is disabled, f(convertedValue), returns true or false\",\n                            \"type\": \"string\",\n                            \"default\": \"return false; //console.log(convertedValue);\"\n                        },\n                        \"customActionFunc\": {\n                            \"title\": \"Method body to action, f(convertedValue, widgetContext, openRightLayout)\",\n                            \"type\": \"string\",\n                            \"default\": \"//self.ctx.stateController.openState(state_id, widgetContext, openRightLayout); //self.ctx.stateController.updateState(...);\"\n                        }\n                    },\n                    \"required\": [\n                        \"text\",\n                        \"isDisabledFunc\",\n                        \"customActionFunc\"\n                    ]\n                }\n            }\n        },\n\n        \"required\": [\n            \"retrievedMethod\",\n            \"convertRetrievedValueFunction\",\n            \"settingList\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrievedMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [{\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call server-dide RPC to get value\"\n                }\n            ]\n        },\n        {\n            \"key\": \"retrievedAttributeKey\",\n            \"condition\": \"model.retrievedMethod === 'attribute' || model.retrievedMethod === 'SHARED_SCOPE' || model.retrievedMethod === 'SERVER_SCOPE'\"\n        },\n        {\n            \"key\": \"retrievedTimeseriesKey\",\n            \"condition\": \"model.retrievedMethod === 'timeseries'\"\n        },\n        {\n            \"key\": \"retrievedRPCMethodName\",\n            \"condition\": \"model.retrievedMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.retrievedMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertRetrievedValueFunction\",\n            \"type\": \"javascript\"\n        },\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: The above are the params related to retrieve value from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"settingList\",\n            \"add\": \"New\",\n            \"style\": {\n                \"add\": \"btn-success\"\n            },\n            \"items\": [\n                \"settingList[].icon\",\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: refer to https://mui.com/material-ui/material-icons for more information about material-icons.</small></I>\"\n                },\n                \"settingList[].text\",\n                {\n                    \"key\": \"settingList[].isDisabledFunc\",\n                    \"type\": \"javascript\"\n                },\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: return true to display this item, return false to not display.</small></I>\"\n                },\n                {\n                    \"key\": \"settingList[].customActionFunc\",\n                    \"type\": \"javascript\"\n                },\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: Update state, open state, or open new dashboard.</small></I>\"\n                }\n            ]\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px 10px\",\"settings\":{\"requestTimeout\":500,\"convertRetrievedValueFunction\":\"return retrievedValue;\",\"settingList\":[{\"icon\":\"date_range\",\"isShowFunc\":\"return true; //console.log(convertedValue);\",\"text\":\"Date\",\"isDisabledFunc\":\"return false; //console.log(convertedValue);\",\"customActionFunc\":\"//self.ctx.stateController.openState(state_id, stateParams, openRightLayout); //self.ctx.stateController.updateState(...);\"},{\"icon\":\"timer\",\"isShowFunc\":\"return true; //console.log(convertedValue);\",\"customActionFunc\":\"//self.ctx.stateController.openState(state_id, stateParams, openRightLayout); //self.ctx.stateController.updateState(...); \",\"text\":\"Time\",\"isDisabledFunc\":\"return false; //console.log(convertedValue);\"}],\"retrievedMethod\":\"rpc\",\"retrievedRPCMethodName\":\"getValue\"},\"title\":\"Setting list\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.rtc_button_with_method_params",
      "name": "RTC button with params & response",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7,
        "sizeY": 4,
        "resources": [],
        "templateHtml": "    <form #rpcForm=\"ngForm\" (submit)=\"sendCommand()\">\r\n      <div class=\"mat-content mat-padding\" fxLayout=\"column\">\r\n        <mat-form-field [fxShow]=\"requireParam\" class=\"mat-block\">\r\n          <mat-label>{{ paramLabel }}</mat-label>  \r\n          <input matInput required name=\"rpcParams\" #rpcParamsField=\"ngModel\" [(ngModel)]=\"rpcParams\"/>\r\n          <mat-error *ngIf=\"rpcParamsField.hasError('required')\">\r\n            RPC params is required.\r\n          </mat-error>\r\n        </mat-form-field>\r\n        <button [disabled]=\"requireParam && (rpcForm.invalid || !rpcForm.dirty)\" mat-raised-button color=\"primary\"  type=\"submit\" >\r\n            {{ buttonLabel }}\r\n        </button>\r\n        <div style=\"margin-top: 18px;\">\r\n          <label>RPC command response</label>\r\n          <div style=\"width: 100%; height: 60px; border: solid 2px gray\" [innerHTML]=\"rpcCommandResponse\">\r\n          </div>\r\n        </div>\r\n      </div>\r\n    </form>\r\n",
        "templateCss": "\n",
        "controllerScript": "self.onInit = function() {\r\n    let utils    = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.requireParam  = settings.requireParam;\r\n    self.ctx.$scope.paramLabel  = settings.paramLabel;\r\n    self.ctx.$scope.buttonLabel = settings.buttonLabel;\r\n    \r\n    self.ctx.$scope.sendCommand = function() {\r\n        //var rpcMethod = self.ctx.$scope.rpcMethod;\r\n        var rpcMethod = self.ctx.settings.rpcMethod;\r\n        var rpcParams = self.ctx.$scope.rpcParams;\r\n        var timeout = self.ctx.settings.requestTimeout;\r\n        var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ? true : false;\r\n        \r\n        //console.log(\"sendCommand()\");\r\n        //console.log(rpcMethod);\r\n        //console.log(rpcParams);\r\n        //console.log(timeout);\r\n        //console.log(oneWayElseTwoWay);\r\n        \r\n        var commandObservable;\r\n        if (oneWayElseTwoWay) {\r\n            commandObservable = self.ctx.controlApi.sendOneWayCommand(rpcMethod, rpcParams, timeout);\r\n        } else {\r\n            commandObservable = self.ctx.controlApi.sendTwoWayCommand(rpcMethod, rpcParams, timeout);\r\n        }\r\n        commandObservable.subscribe(\r\n            function (response) {\r\n                if (oneWayElseTwoWay) {\r\n                    self.ctx.$scope.rpcCommandResponse = \"Command was successfully received by device.<br> No response body because of one way command mode.\";\r\n                } else {\r\n                    self.ctx.$scope.rpcCommandResponse = \"Response from device:<br>\";                    \r\n                    self.ctx.$scope.rpcCommandResponse += JSON.stringify(response, undefined, 2);\r\n                }\r\n                self.ctx.detectChanges();\r\n            },\r\n            function (rejection) {\r\n                self.ctx.$scope.rpcCommandResponse = \"Failed to send command to the device:<br>\"\r\n                self.ctx.$scope.rpcCommandResponse += \"Status: \" + rejection.status + \"<br>\";\r\n                self.ctx.$scope.rpcCommandResponse += \"Status text: '\" + rejection.statusText + \"'\";\r\n                self.ctx.detectChanges();\r\n            }\r\n            \r\n        );\r\n    }\r\n    \r\n}\r\n\r\n\r\n/* \r\n        <mat-form-field class=\"mat-block\">\r\n          <mat-label>RPC method</mat-label>\r\n          <input matInput required name=\"rpcMethod\" #rpcMethodField=\"ngModel\" [(ngModel)]=\"rpcMethod\"/>\r\n          <mat-error *ngIf=\"rpcMethodField.hasError('required')\">\r\n            RPC method name is required.\r\n          </mat-error>\r\n        </mat-form-field>\r\n*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"rpcMethod\": {\r\n                \"title\": \"RPC method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpcMethod\"\r\n            },\r\n            \"oneWayElseTwoWay\": {\r\n                \"title\": \"Is One Way Command\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"requireParam\": {\r\n                \"title\": \"Is RPC param required\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"paramLabel\": {\r\n                \"title\": \"RPC param label\",\r\n                \"type\": \"string\",\r\n                \"default\": \"RPC param label\"\r\n            },\r\n            \"buttonLabel\": {\r\n                \"title\": \"Button label\",\r\n                \"type\": \"string\",\r\n                \"default\": \"Send RPC Command\"\r\n            }\r\n        },\r\n        \"required\": [\"rpcMethod\",\r\n            \"paramLabel\",\r\n            \"buttonLabel\"\r\n            ]\r\n    },\r\n    \"form\": [\r\n        \"rpcMethod\",\r\n        \"oneWayElseTwoWay\",\r\n        \"requestTimeout\",\r\n        \"requireParam\",\r\n        \"paramLabel\",\r\n        \"buttonLabel\"\r\n        ]\r\n} ",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"rpcMethod\":\"rpcMethod\",\"oneWayElseTwoWay\":true,\"paramLabel\":\"RPC param label\",\"buttonLabel\":\"Send RPC Command\",\"requireParam\":true},\"title\":\"RTC button with params & response\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_with_state_parameter_from_flexiable_option2",
      "name": "Select double value from flexiable option (New)",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 8,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '5px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\"> \r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-weight: 500;\r\n    white-space: nowrap;\r\n    margin: 10px 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 4px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}",
        "controllerScript": "// v.1.0.0, 2023-05-23 11:52\r\n\r\n// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n\r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttrKey = {};\r\n    retrieveAttrKey.adjusted = settings.retrieveAttributes.adjusted;//let retrieveAttributeKey            = settings.retrieveAttributeKey;\r\n    retrieveAttrKey.min      = settings.retrieveAttributes.min;     //let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    retrieveAttrKey.max      = settings.retrieveAttributes.max;     //let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    retrieveAttrKey.step     = settings.retrieveAttributes.step;    //let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    retrieveAttrKey.unit     = settings.retrieveAttributes.unit;    //let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    \r\n    //console.log(\"settings\", settings);\r\n    \r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue = {};\r\n\r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttrKey.adjusted && keyName === retrieveAttrKey.adjusted) {\r\n                    /*try { \r\n                        attrValue.adjusted = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.adjusted = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttrKey.min && keyName === retrieveAttrKey.min) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.min = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.max && keyName === retrieveAttrKey.max) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.max = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.step && keyName === retrieveAttrKey.step) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue.step = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttrKey.unit && keyName === retrieveAttrKey.unit) {\r\n                    attrValue.unit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValue.min, attrValue.max, attrValue.step, attrValue.unit, attrValue.adjusted);\r\n        //console.log(lastAttrValue.min, lastAttrValue.max, lastAttrValue.step, lastAttrValue.unit);\r\n        if (attrValue.min !== lastAttrValue.min || attrValue.max !== lastAttrValue.max ||\r\n            attrValue.step !== lastAttrValue.step || attrValue.unit !== lastAttrValue.unit) {\r\n\r\n            if (attrValue.min)  lastAttrValue.min = attrValue.min;\r\n            if (attrValue.max)  lastAttrValue.max = attrValue.max;\r\n            if (attrValue.step) lastAttrValue.step = attrValue.step; \r\n            if (attrValue.unit) lastAttrValue.unit = attrValue.unit;\r\n            \r\n            if (attrValue.min && attrValue.max && attrValue.step) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValue.min;\r\n                let fMax = factor*attrValue.max;\r\n                let fStep = factor*attrValue.step;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValue.min, i=0; val<factor*attrValue.max+factor*attrValue.step; i++, val=factor*attrValue.min+factor*attrValue.step*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValue.unit && attrValue.unit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValue.unit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue.adjusted) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue.adjusted);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue.adjusted, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttrKey.adjusted && retrieveAttrKey.adjusted.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.adjusted } );\r\n            }\r\n            if (retrieveAttrKey.min && retrieveAttrKey.min.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.min } );\r\n            }\r\n            if (retrieveAttrKey.max && retrieveAttrKey.max.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.max } );\r\n            }\r\n            if (retrieveAttrKey.step && retrieveAttrKey.step.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.step } );\r\n            }\r\n            if (retrieveAttrKey.unit && retrieveAttrKey.unit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttrKey.unit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateTimeseriesKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"retrieveAttributes\": {\n                \"type\": \"object\",\n                \"title\": \"Retrieve device attributes\",\n                \"properties\": {\n                    \"adjusted\": {\n                        \"title\": \"Adjusted device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"min\": {\n                        \"title\": \"Min value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"max\": {\n                        \"title\": \"Max value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"step\": {\n                        \"title\": \"Step value of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    },\n                    \"unit\": {\n                        \"title\": \"Unit of device attribute\",\n                        \"type\": \"string\",\n                        \"default\": \"\"\n                    }\n                },\n                \"required\": [\n                    \"adjusted\",\n                    \"min\",\n                    \"max\",\n                    \"step\",\n                    \"unit\"\n                ]\n            },\n\n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Key of attribute being updated\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateTimeseriesKey\": {\n                \"title\": \"Key of timeseries value being updated(?)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using client-side RPC method\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            }\n        },\n\n        \"required\": [\n            \"retrieveAttributes\",\n            \"updateMethod\",\n            \"convertValueFunction\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        \"retrieveAttributes\",\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [{\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        {\n            \"key\": \"updateAttributeKey\",\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\n        },\n        {\n            \"key\": \"updateTimeseriesKey\",\n            \"condition\": \"model.updateMethod === 'timeseries'\"\n        },\n        {\n            \"key\": \"updateRPCMethod\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.updateMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"retrieveAttributes\":{\"adjusted\":\"adjusted\",\"min\":\"min\",\"max\":\"max\",\"step\":\"step\",\"unit\":\"unit\"},\"updateMethod\":\"rpc\",\"convertValueFunction\":\"return value; /* console.log(value); */\",\"updateRPCMethod\":\"setValue\",\"updateAttributeKey\":\"value\"},\"title\":\"Select double value from flexiable option (New)\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"enableDataExport\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_shared_integer_attribute_with_segmented_switch_",
      "name": "Update shared string attribute with segmented switch ",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 3,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{titleTemplate}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"{{optionStyle?.orientation}}\" fxLayoutAlign=\"center center\"> \r\n            <button mat-button *ngFor=\"let x of options\" (click)=\"updateAttribute(x)\"\r\n                [class.mat-raised-button]=\"optionStyle?.isRaised\"\r\n                [class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"\r\n                [color]=\"(x.attributeValue===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" \r\n                [ngStyle]=\"x.attributeValue===currentValue ? customCheckedStyle : customUncheckedStyle\">\r\n                {{x?.optionLabel}}\r\n            </button>\r\n\r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n/* TC 20230525 */\r\n.tb-rpc-button .button-container .mat-mdc-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n    self.ctx.ngZone.run(function() {\n       init(); \n       self.ctx.detectChanges(true);\n    });\n};\n\n\nfunction init() {\n\n    $scope = self.ctx.$scope;\n    attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n    utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n    translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n    $scope.toastTargetId = 'input-widget' + utils.guid(); // TODO: comment it?\n    settings = utils.deepClone(self.ctx.settings) || {};\n    ////settings.showLabel = utils.defaultValue(settings.showLabel, true);\n    settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n    ////settings.isRequired = utils.defaultValue(settings.isRequired, true);\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    //// $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 (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    $scope.showTitle = settings.widgetTitle && settings.widgetTitle.length ? true : false;\n    $scope.options = settings.options;\n    $scope.optionStyle = settings.optionStyle;\n\n    $scope.customCheckedStyle = {};\n    $scope.customCheckedStyle['min-width'] = '40px';\n    $scope.customCheckedStyle['padding']   = '0 2px';\n    if (settings.optionStyle.orientation === 'column') {\n        $scope.customCheckedStyle['width']     = '100%';\n        $scope.customCheckedStyle['margin']    = 0;\n    } else {\n        $scope.customCheckedStyle['width']     = 100/settings.options.length + '%';\n        $scope.customCheckedStyle['margin']    = 0;\n    }\n    if (settings.optionStyle.checkedOption.isPrimary === false) {\n        $scope.customCheckedStyle['background-color']  = $scope.optionStyle.checkedOption.bgColor;\n        $scope.customCheckedStyle['color']             = $scope.optionStyle.checkedOption.textColor;\n    }\n\n    $scope.customUncheckedStyle = {};\n    $scope.customUncheckedStyle['min-width'] = '40px';\n    $scope.customUncheckedStyle['padding']   = '0 2px';\n    if (settings.optionStyle.orientation === \"column\") {\n        $scope.customUncheckedStyle['width']     = '100%';\n        $scope.customUncheckedStyle['margin']    = 0;\n    } else {\n        $scope.customUncheckedStyle['width']     = 100/settings.options.length + '%';\n        $scope.customUncheckedStyle['margin']    = 0;\n    }\n    if (settings.optionStyle.uncheckedOption.isPrimary === false) {\n        $scope.customUncheckedStyle['background-color']  = $scope.optionStyle.uncheckedOption.bgColor;\n        $scope.customUncheckedStyle['color']             = $scope.optionStyle.uncheckedOption.textColor;\n    }\n    \n    // var validators = [\n    //     $scope.validators.min(settings.minValue),\n    //     $scope.validators.max(settings.maxValue),\n    //     $scope.validators.pattern(/^-?[0-9]+$/)\n    // ];\n    \n    // if (settings.isRequired) {\n    //     validators.push($scope.validators.required);\n    // }\n\n    // $scope.attributeUpdateFormGroup = $scope.fb.group({\n    //     currentValue: [undefined, validators]\n    // });\n\n    if (self.ctx.datasources && self.ctx.datasources.length) {\n        var datasource = self.ctx.datasources[0];\n        if (datasource.type === 'entity') {\n            if (datasource.entityType === 'DEVICE') {\n                if (datasource.entityType && datasource.entityId) {\n                    $scope.entityName = datasource.entityName;\n                    $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 !== \"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    // update value at click a button\n    $scope.updateAttribute = function(option) {\n        $scope.isFocused = false;\n        if ($scope.entityDetected) {\n            var datasource = self.ctx.datasources[0];\n\n            attributeService.saveEntityAttributes(\n                datasource.entity.id,\n                'SHARED_SCOPE',\n                [\n                    {\n                        key: $scope.currentKey,\n                        value: option.attributeValue //$scope.attributeUpdateFormGroup.get('currentValue').value\n                    }\n                ]\n            ).subscribe(\n                function success() {\n                    $scope.originalValue = $scope.currentValue; //$scope.attributeUpdateFormGroup.get('currentValue').value;\n                    if (settings.showResultMessage) {\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n                    }\n                },\n                function fail() {\n                    if (settings.showResultMessage) {\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n                    }\n                }\n            );\n        }\n    };\n\n    // $scope.changeFocus = function () {\n    //     if ($scope.currentValue === $scope.originalValue) {  // $scope.attributeUpdateFormGroup.get('currentValue').value\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.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n                self.ctx.detectChanges();\n            }\n        }\n    } catch (e) {\n        console.log(e);\n    }\n}\n\n// function correctValue(value) {\n//     if (typeof value !== \"string\") { // \"number\"\n//         return 0;\n//     }\n//     return value;\n// }\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        maxDataKeys: 1,\n        singleEntity: true\n    }\n}\n\nself.onDestroy = function() {\n\n}",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"widgetTitle\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n\r\n            \"options\":{\r\n                \"type\": \"array\",\r\n                \"title\": \"Options\",\r\n                \"minItems\": 1,\r\n                \"maxItems\": 30,\r\n                \"items\": {\r\n                    \"type\": \"object\",\r\n                    \"properties\": {\r\n                        \"optionLabel\": {\r\n                            \"title\": \"Option label\",\r\n                            \"type\": \"string\",\r\n                            \"default\": \"Option label\"\r\n                        },\r\n                        \"attributeValue\": {\r\n                            \"title\": \"Attribute value\",\r\n                            \"type\": \"string\",\r\n                            \"description\": \"Attribute value.\"\r\n                        }\r\n                    }, \r\n                    \"required\": [\r\n                        \"optionLabel\",\r\n                        \"attributeValue\"\r\n                    ]\r\n                }\r\n            },\r\n            \"optionStyle\":{\r\n                \"type\": \"object\",\r\n                \"title\": \"Option Style\",\r\n                \"properties\": {\r\n                    \"orientation\": {\r\n                        \"type\": \"string\",\r\n                        \"title\": \"Orientation\",\r\n                        \"default\": \"column\"\r\n                    },\r\n                    \"isRaised\": {\r\n                        \"type\": \"boolean\",\r\n                        \"title\": \"Raised\",\r\n                        \"default\": true\r\n                    },\r\n                    \"checkedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Checked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Checked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    },\r\n                    \"uncheckedOption\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Unchecked Option Style\",\r\n                        \"properties\": {\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Unchecked option text color\",\r\n                                \"default\": null\r\n                            }\r\n                        }\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"orientation\",\r\n                    \"isRaised\",\r\n                    \"checkedOption\",\r\n                    \"uncheckedOption\"\r\n                ]\r\n            },\r\n            \r\n            \"required\": [\r\n                \"options\",\r\n                \"optionStyle\"\r\n            ]\r\n        }\r\n    },\r\n    \"form\": [\r\n        \"widgetTitle\",\r\n        \"showResultMessage\",\r\n\r\n        {\r\n            \"key\": \"options\",\r\n            \"items\": [\r\n                \"options[]\"\r\n            ]\r\n        }, \r\n        {\r\n            \"key\": \"optionStyle\",\r\n            \"items\" :[\r\n                {\r\n                    \"key\": \"optionStyle.orientation\",\r\n                    \"type\": \"rc-select\",\r\n                    \"multiple\": false,\r\n                    \"items\": [{\r\n                        \"value\": \"column\",\r\n                        \"label\": \"Column\"\r\n                    }, {\r\n                        \"value\": \"row\",\r\n                        \"label\": \"Row\"\r\n                    }]\r\n                },\r\n                \"optionStyle.isRaised\",\r\n                {\r\n                    \"key\": \"optionStyle.checkedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.checkedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.checkedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }, {\r\n                    \"key\": \"optionStyle.uncheckedOption\",\r\n                    \"items\": [\r\n                        \"optionStyle.uncheckedOption.isPrimary\",\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.bgColor\",\r\n                            \"type\": \"color\"\r\n                        },\r\n                        {\r\n                            \"key\": \"optionStyle.uncheckedOption.textColor\",\r\n                            \"type\": \"color\"\r\n                        }\r\n                    ]\r\n                }\r\n            ]\r\n        }\r\n    ]\r\n}\r\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\":\"#FFFFFF\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false,\"bgColor\":\"#e6e7e8\"}},\"options\":[{\"optionLabel\":\"Option label 0\",\"attributeValue\":\"Option label 0\"},{\"optionLabel\":\"Option label 1\",\"attributeValue\":\"Option label 1\"}],\"showResultMessage\":true},\"title\":\"Update shared string attribute with segmented switch \",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_timezone_value",
      "name": "Select timezone value",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\n//[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\"\r\n\r\nlet timezones = [   {\"name\":\"UTC-12:00\", \"offset\": -(12*60)},\r\n                    {\"name\":\"UTC-11:00\", \"offset\": -(11*60)},\r\n                    {\"name\":\"UTC-10:00\", \"offset\": -(10*60)},\r\n                    {\"name\":\"UTC-09:30\", \"offset\": -( 9*60 + 30)},\r\n                    {\"name\":\"UTC-09:00\", \"offset\": -( 9*60)},\r\n                    {\"name\":\"UTC-08:00\", \"offset\": -( 8*60)},\r\n                    {\"name\":\"UTC-07:00\", \"offset\": -( 7*60)},\r\n                    {\"name\":\"UTC-06:00\", \"offset\": -( 6*60)},\r\n                    {\"name\":\"UTC-05:00\", \"offset\": -( 5*60)},\r\n                    {\"name\":\"UTC-04:00\", \"offset\": -( 4*60)},\r\n                    {\"name\":\"UTC-03:30\", \"offset\": -( 3*60 + 30)},\r\n                    {\"name\":\"UTC-03:00\", \"offset\": -( 3*60)},\r\n                    {\"name\":\"UTC-02:00\", \"offset\": -( 2*60)},\r\n                    {\"name\":\"UTC-01:00\", \"offset\": -( 1*60)},\r\n                    {\"name\":\"UTC\",       \"offset\":  ( 0*60)},\r\n                    {\"name\":\"UTC+00:00\", \"offset\":  ( 0*60)},\r\n                    {\"name\":\"UTC+01:00\", \"offset\":  ( 1*60)},\r\n                    {\"name\":\"UTC+02:00\", \"offset\":  ( 2*60)},\r\n                    {\"name\":\"UTC+03:00\", \"offset\":  ( 3*60)},\r\n                    {\"name\":\"UTC+03:30\", \"offset\":  ( 3*60 + 30)},\r\n                    {\"name\":\"UTC+04:00\", \"offset\":  ( 4*60)},\r\n                    {\"name\":\"UTC+04:30\", \"offset\":  ( 4*60 + 30)},\r\n                    {\"name\":\"UTC+05:00\", \"offset\":  ( 5*60)},\r\n                    {\"name\":\"UTC+05:30\", \"offset\":  ( 5*60 + 30)},\r\n                    {\"name\":\"UTC+06:00\", \"offset\":  ( 6*60)},\r\n                    {\"name\":\"UTC+06:30\", \"offset\":  ( 6*60 + 30)},\r\n                    {\"name\":\"UTC+07:00\", \"offset\":  ( 7*60)},\r\n                    {\"name\":\"UTC+08:00\", \"offset\":  ( 8*60)},\r\n                    {\"name\":\"UTC+09:00\", \"offset\":  ( 9*60)},\r\n                    {\"name\":\"UTC+09:30\", \"offset\":  ( 9*60 + 30)},\r\n                    {\"name\":\"UTC+10:00\", \"offset\":  (10*60)},\r\n                    {\"name\":\"UTC+10:30\", \"offset\":  (10*60 + 30)},\r\n                    {\"name\":\"UTC+11:00\", \"offset\":  (11*60)},\r\n                    {\"name\":\"UTC+12:00\", \"offset\":  (12*60)},\r\n                    {\"name\":\"UTC+12:45\", \"offset\":  (12*60 + 45)},\r\n                    {\"name\":\"UTC+13:00\", \"offset\":  (13*60)},\r\n                    {\"name\":\"UTC+14:00\", \"offset\":  (14*60)},\r\n                ];\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries \r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!';\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                } else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey);\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveMethod\": {\n                \"title\": \"Retrieve timezone(integer value) using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrieveAttributeKey\": {\n                \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveRPCMethod\": {\n                \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"parseValueFunction\": {\n                \"title\": \"Parse value function, f(data), returns timezone(integer value)\",\n                \"type\": \"string\",\n                \"default\": \"return data; /* console.log(data); */ \"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update timezone(integer value) using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n\n            \"required\": [\n                \"retrieveMethod\",\n                \"updateMethod\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrieveMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC get value method\"\n                }\n            ]\n        },\n        \"retrieveAttributeKey\",\n        \"retrieveRPCMethod\",\n        {\n            \"key\": \"parseValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Select timezone value\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.styled_button_of_string_value_with_pattern_key",
      "name": "Styled button of string value with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 10,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\"\r\n    style=\"width: 100%; height: 100%;\">\r\n\r\n    <div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n        <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n            class=\"title-container\" fxLayout=\"row\"\r\n            fxLayoutAlign=\"start center\"\r\n            [fxShow]=\"showTitle\">\r\n            <span class=\"button-title\">{{title}}</span>\r\n        </div>\r\n\r\n        <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n            class=\"button-container\" fxLayout=\"column\"\r\n            fxLayoutAlign=\"center center\">\r\n\r\n                <button mat-button (click)=\"updateValue(buttonState?.on.value===currentValue)\"\r\n                    [class.mat-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                    [class.mat-mdc-raised-button]=\"buttonState?.on.value===currentValue ? buttonState?.on.isRaised : buttonState?.off.isRaised\"\r\n                    [color]=\"(buttonState?.on.value===currentValue ? buttonState?.on.isPrimary : buttonState?.off.isPrimary) ? 'primary' : ''\" \r\n                    [ngStyle]=\"buttonState?.on.value===currentValue ? customOnStyle : customOffStyle\">\r\n                    {{buttonState?.on.value===currentValue ? buttonState?.on.label : buttonState?.off.label}}\r\n                </button>\r\n\r\n        </div>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n/*TC 20230529 for mat-mdc-raised-button*/\r\n.tb-rpc-button .button-container .mat-mdc-raised-button {\r\n    width: 100%;\r\n}\r\n\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\r\n.tb-rpc-button .show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n",
        "controllerScript": "// v.1.0.0, TC 2023-05-29\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n    self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    self.ctx.$scope.title = self.ctx.settings.title;\r\n    self.ctx.$scope.buttonState = settings.buttonState;\r\n    if (settings.buttonState.on.isPrimary === false) {\r\n            self.ctx.$scope.customOnStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.on.bgColor,\r\n                'color': self.ctx.$scope.buttonState.on.textColor,\r\n            };\r\n    }\r\n    if (settings.buttonState.off.isPrimary === false) {\r\n            self.ctx.$scope.customOffStyle = {\r\n                'background-color': self.ctx.$scope.buttonState.off.bgColor,\r\n                'color': self.ctx.$scope.buttonState.off.textColor,\r\n            };\r\n    }\r\n\r\n    //console.log(\"onInit\", settings);\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n\r\n    function parseValue(data) {\r\n        var parsed = data;\r\n        if (typeof settings.buttonState.on.value === \"string\") {\r\n            if (typeof data === \"boolean\") {\r\n                //parsed = data===true ? \"true\" : \"false\";\r\n                parsed = data===true ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            } else if (typeof data === \"number\") {\r\n                //parsed = data===1 ? \"true\" : \"false\";\r\n                parsed = data!==0 ? settings.buttonState.on.value : settings.buttonState.off.value;\r\n                \r\n            }\r\n        } else if (typeof settings.buttonState.on.value === \"boolean\") {\r\n            if (typeof data === \"string\") {\r\n                parsed = data===\"true\" ? true : false;\r\n            } else if (typeof data === \"number\") {\r\n                parsed = data!==0 ? true : false;\r\n            }\r\n        }\r\n        return parsed;\r\n    }\r\n\r\n    let patternParamOfetrievedAttributeKeyOrMethodName = \" \";\r\n    if (settings.retrieveMethod === \"attribute\" || settings.retrieveMethod === \"SHARED_SCOPE\" || settings.retrieveMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveAttributeKey;\r\n    } else if (settings.retrieveMethod === \"timeseries\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveTimeseriesKey;\r\n    } else if (settings.retrieveMethod === \"rpc\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveRPCMethodName;\r\n    }\r\n\r\n    let convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertRetrievedAttributeKeyOrMethodNameFunction && settings.convertRetrievedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfetrievedAttributeKeyOrMethodName', 'patternCommonParam', settings.convertRetrievedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let retrievedAttributeKeyOrMethodName = convertRetrievedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertRetrievedValueFunction = parseValue; //(value) => value;  // Retrieve data\r\n    if (settings.convertRetrievedValueFunction && settings.convertRetrievedValueFunction.length) {\r\n        try {\r\n            convertRetrievedValueFunction = new Function('value', settings.convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = parseValue; //(value) => value；\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    \r\n    let patternParamOfUpdateAttributeKeyOrMethodName = \" \";\r\n    if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateAttributeKey;\r\n    } else if (settings.updateMethod === \"timeseries\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateTimeseriesKey;\r\n    } else if (settings.updateMethod === \"rpc\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    }\r\n\r\n    let convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertUpdatedAttributeKeyOrMethodNameFunction && settings.convertUpdatedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfUpdateAttributeKeyOrMethodName', 'patternCommonParam', settings.convertUpdatedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let updateAttributeKeyOrMethodName = convertUpdatedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertUpdatedValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertUpdatedValueFunction && settings.convertUpdatedValueFunction.length) {\r\n        try {\r\n            convertUpdatedValueFunction = new Function('value', settings.convertUpdatedValueFunction);\r\n        } catch (e) {\r\n            convertUpdatedValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                ////if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                ////}\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n\r\n    /*function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }*/\r\n\r\n    function subscribeAttributes(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n\r\n        //if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [\r\n        //        {name: key}\r\n        //    ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    function subscribeTimeseries(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [\r\n        //        {name: key}\r\n        //    ];\r\n        //} else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrievedAttributeKeyOrMethodName is null.\";\r\n            $scope.showErrorToast(translate.instant('retrievedAttributeKeyOrMethodName is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(on_off) {\r\n        //console.log(\"updateValue()\", settings);\r\n\r\n        let value = on_off ? settings.buttonState.off.value : \r\n            settings.buttonState.on.value;\r\n        let newValue = convertUpdatedValueFunction(value);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateTimeseriesKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateRPCMethodName, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(retrievedAttributeKeyOrMethodName, settings.requestTimeout); //settings.patternParamOfRetrieveRPCMethodName\r\n                } else if (settings.retrieveMethod == 'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveAttributeKey\r\n                    }\r\n                } else if (settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveTimeseriesKey\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n\r\n            \"patternCommonParam\": {\r\n                \"title\": \"Pattern common param\",\r\n                \"type\": \"string\",\r\n                \"default\": \"0\"\r\n            },\r\n\r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfRetrieveAttributeKey\": {\r\n                \"title\": \"Pattern param of retrieved Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveTimeseriesKey\": {\r\n                \"title\": \"Pattern param of retrieved Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveRPCMethodName\": {\r\n                \"title\": \"Pattern param of retrieved value using server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\": {\r\n                \"title\": \"Convert function, f(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam), returns retireved attribute key, time-series data key or server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n            },\r\n            \"convertRetrievedValueFunction\": {\r\n                \"title\": \"Parse value function, f(value), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfUpdateAttributeKey\": {\r\n                \"title\": \"Pattern param of updated Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateTimeseriesKey\": {\r\n                \"title\": \"Pattern param of updated Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateRPCMethodName\": {\r\n                \"title\": \"Pattern param of updated server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\": {\r\n                \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam), returns updated attribute key, time-series data key or server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n            },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"convertUpdatedValueFunction\": {\r\n                \"title\": \"Convert function, f(value), returns updated value\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"buttonState\": {\r\n                \"type\": \"object\",\r\n                \"title\": \"Button state\",\r\n                \"properties\": {\r\n                    \"on\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"On state\",\r\n                        \"properties\": {\r\n                            \"value\": {\r\n                                \"title\": \"On state attribute value\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"true\",\r\n                                \"description\": \"On state attribute value.\"\r\n                            },\r\n                            \"label\": {\r\n                                \"title\": \"On state label\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"On state Label\"\r\n                            },\r\n                            \"isRaised\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Raised\",\r\n                                \"default\": true\r\n                            },\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": true\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"On state background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"On state text color\",\r\n                                \"default\": null\r\n                            }\r\n                        },\r\n                        \"required\": [\r\n                            \"value\",\r\n                            \"label\"\r\n                        ]\r\n                    },\r\n                    \"off\": {\r\n                        \"type\": \"object\",\r\n                        \"title\": \"Off state\",\r\n                        \"properties\": {\r\n                            \"value\": {\r\n                                \"title\": \"Off state attribute value\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"false\",\r\n                                \"description\": \"Off state attribute value.\"\r\n                            },\r\n                            \"label\": {\r\n                                \"title\": \"Off state label\",\r\n                                \"type\": \"string\",\r\n                                \"default\": \"Off state label\"\r\n                            },\r\n                            \"isRaised\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Raised\",\r\n                                \"default\": true\r\n                            },\r\n                            \"isPrimary\": {\r\n                                \"type\": \"boolean\",\r\n                                \"title\": \"Primary color\",\r\n                                \"default\": false\r\n                            },\r\n                            \"bgColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Off state background color\",\r\n                                \"default\": null\r\n                            },\r\n                            \"textColor\": {\r\n                                \"type\": \"string\",\r\n                                \"title\": \"Off state text color\",\r\n                                \"default\": null\r\n                            }\r\n                        },\r\n                        \"required\": [\r\n                            \"value\",\r\n                            \"label\"\r\n                        ]\r\n                    }\r\n                },\r\n                \"required\": [\r\n                    \"on\",\r\n                    \"off\"\r\n                ]\r\n            },\r\n            \"showResultMessage\": {\r\n                \"title\": \"Show result message\",\r\n                \"type\": \"boolean\",\r\n                \"default\": true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"patternCommonParam\",\r\n            \"retrieveMethod\",\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"updateMethod\",\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n            \"requestTimeout\",\r\n            \"buttonState\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"patternCommonParam\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [{\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-dide RPC to get value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveAttributeKey\",\r\n            \"condition\": \"model.retrieveMethod === 'attribute' || model.retrieveMethod === 'SHARED_SCOPE' || model.retrieveMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveTimeseriesKey\",\r\n            \"condition\": \"model.retrieveMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveRPCMethodName\",\r\n            \"condition\": \"model.retrieveMethod === 'rpc'\"\r\n        },\r\n        {\r\n            \"key\": \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"key\": \"convertRetrievedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\r\n        },\r\n\r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [{\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-side RPC to set value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateAttributeKey\",\r\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\r\n            \"condition\": \"model.updateMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\r\n            \"condition\": \"model.updateMethod === 'rpc'\"\r\n        },\r\n        {\r\n            \"key\": \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"key\": \"convertUpdatedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\r\n        },\r\n\r\n        \"requestTimeout\",\r\n\r\n        {\r\n            \"key\": \"buttonState\",\r\n            \"items\": [{\r\n                \"key\": \"buttonState.on\",\r\n                \"items\": [\r\n                    \"buttonState.on.value\",\r\n                    \"buttonState.on.label\",\r\n                    \"buttonState.on.isRaised\",\r\n                    \"buttonState.on.isPrimary\",\r\n                    {\r\n                        \"key\": \"buttonState.on.bgColor\",\r\n                        \"type\": \"color\"\r\n                    }, {\r\n                        \"key\": \"buttonState.on.textColor\",\r\n                        \"type\": \"color\"\r\n                    }\r\n                ]\r\n            }, {\r\n                \"key\": \"buttonState.off\",\r\n                \"items\": [\r\n                    \"buttonState.off.value\",\r\n                    \"buttonState.off.label\",\r\n                    \"buttonState.off.isRaised\",\r\n                    \"buttonState.off.isPrimary\",\r\n                    {\r\n                        \"key\": \"buttonState.off.bgColor\",\r\n                        \"type\": \"color\"\r\n                    }, {\r\n                        \"key\": \"buttonState.off.textColor\",\r\n                        \"type\": \"color\"\r\n                    }\r\n                ]\r\n            }]\r\n        },\r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"requestTimeout\":5000,\"retrieveMethod\":\"rpc\",\"updateMethod\":\"rpc\",\"buttonState\":{\"on\":{\"label\":\"On state Label\",\"isRaised\":true,\"isPrimary\":true,\"value\":\"True\"},\"off\":{\"value\":\"false\",\"label\":\"Off state label\",\"isRaised\":true,\"isPrimary\":false}},\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\"},\"title\":\"Styled button of string value with pattern key\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.select_double_value_from_flexiable_options",
      "name": "Select double value from flexiable options",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 7.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\r\n\r\n\t\t\t<select [(ngModel)]=\"currentValue\" (change)=\"updateValue()\" \r\n\t\t\t\t[ngStyle]=\"customStyle\" \r\n\t\t\t\taria-label=\"Pick a value\" >\r\n\t\t\t\t\r\n                <option *ngFor=\"let x of options\" [value]=\"x.attributeValue\">\r\n                    {{x.optionLabel}}\r\n                </option>\r\n\t\t\t\t\r\n            </select>\r\n            \r\n    </div>\r\n    <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n         fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>\r\n",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container select {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    //self.ctx.$scope.options = settings.options;\r\n    self.ctx.$scope.options = [];\r\n    /*for (const item of timezones) {\r\n        self.ctx.$scope.options.push({\r\n            \"optionLabel\": item.name,\r\n            \"attributeValue\": item.offset\r\n        });\r\n    }*/\r\n    \r\n    //[ngStyle]=\"{'color': currentValue!==originalValue ? 'rgba(255,0,0,0.87)' : 'none'}\" \r\n    /*function refreshTextColor() {\r\n        if (self.ctx.$scope.currentValue!==self.ctx.$scope.originalValue) {\r\n            self.ctx.$scope.customStyle = {'color': 'rgba(255,0,0,0.87)'};\r\n        } else {\r\n            self.ctx.$scope.customStyle = {'color': 'none'};\r\n        }\r\n        console.log(\"refreshTextColor():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, self.ctx.$scope.customStyle);\r\n    };*/\r\n    \r\n    //console.log(\"onInit\", settings);\r\n    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = \"0\";\r\n    //refreshTextColor();\r\n    \r\n    let parseValueFunction = (data) => data;        // Retrieve data\r\n    if (settings.parseValueFunction && settings.parseValueFunction.length) {\r\n        try {\r\n            parseValueFunction = new Function('data', settings.parseValueFunction);\r\n        } catch (e) {\r\n            parseValueFunction = (data) => data;\r\n        }\r\n    }\r\n    \r\n    let convertValueFunction = (value) => value;    // Update data\r\n    if (settings.convertValueFunction && settings.convertValueFunction.length) {\r\n        try {\r\n            convertValueFunction = new Function('value', settings.convertValueFunction);\r\n        } catch (e) {\r\n            convertValueFunction = (value) => value;\r\n        }\r\n    }\r\n    \r\n    // subscribe attribute & timeseries \r\n    let retrieveAttributeKey            = settings.retrieveAttributeKey;\r\n    let retrieveAttributeKeyOfMinValue  = settings.retrieveAttributeKeyOfMinValue;\r\n    let retrieveAttributeKeyOfMaxValue  = settings.retrieveAttributeKeyOfMaxValue;\r\n    let retrieveAttributeKeyOfStepValue = settings.retrieveAttributeKeyOfStepValue;\r\n    let retrieveAttributeKeyOfUnit      = settings.retrieveAttributeKeyOfUnit;\r\n    let lastAttrValue;\r\n    let lastAttrValueOfMin;\r\n    let lastAttrValueOfMax;\r\n    let lastAttrValueOfStep;\r\n    let lastAttrValueOfUnit;\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        let attrValue;\r\n        let attrValueOfMin;\r\n        let attrValueOfMax;\r\n        let attrValueOfStep;\r\n        let attrValueOfUnit;\r\n        \r\n        for (let i=0; subscription.data && i<subscription.data.length; i++) {\r\n            let keyName  = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription.data[i].data.length>0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                if (retrieveAttributeKey && keyName === retrieveAttributeKey) {\r\n                    /*try { \r\n                        attrValue = parseValueFunction(KeyValue);  ////angular.fromJson(newValue)\r\n                    } catch (e){\r\n                        self.ctx.$scope.error = 'parseValueFunction(value) error!'\r\n                    }*/\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValue = KeyValue;\r\n                    }\r\n                    \r\n                } else if (retrieveAttributeKeyOfMinValue && keyName === retrieveAttributeKeyOfMinValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfMin = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfMaxValue && keyName === retrieveAttributeKeyOfMaxValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfMax = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfStepValue && keyName === retrieveAttributeKeyOfStepValue) {\r\n                    if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                        attrValueOfStep = KeyValue;\r\n                    }\r\n    \r\n                } else if (retrieveAttributeKeyOfUnit && keyName === retrieveAttributeKeyOfUnit) {\r\n                    attrValueOfUnit = String(KeyValue);\r\n    \r\n                } else {\r\n                    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                    console.log(subscription.data[i]);\r\n                }\r\n            }\r\n        }\r\n        \r\n        //console.log(attrValueOfMin, attrValueOfMax, attrValueOfStep, attrValueOfUnit, attrValue);\r\n        //console.log(lastAttrValueOfMin, lastAttrValueOfMax, lastAttrValueOfStep, lastAttrValueOfUnit);\r\n        if (attrValueOfMin !== lastAttrValueOfMin || attrValueOfMax !== lastAttrValueOfMax ||\r\n            attrValueOfStep !== lastAttrValueOfStep || attrValueOfUnit !== lastAttrValueOfUnit) {\r\n\r\n            if (attrValueOfMin)  lastAttrValueOfMin = attrValueOfMin;\r\n            if (attrValueOfMax)  lastAttrValueOfMax = attrValueOfMax;\r\n            if (attrValueOfStep) lastAttrValueOfStep = attrValueOfStep; \r\n            if (attrValueOfUnit) lastAttrValueOfUnit = attrValueOfUnit;\r\n            \r\n            if (attrValueOfMin && attrValueOfMax && attrValueOfStep) {\r\n                let factor = 100;\r\n                if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                    factor = Math.pow(10, self.ctx.widgetConfig.decimals);\r\n                }\r\n                \r\n                self.ctx.$scope.options = [];\r\n                let fMin = factor*attrValueOfMin;\r\n                let fMax = factor*attrValueOfMax;\r\n                let fStep = factor*attrValueOfStep;\r\n                for (let val=fMin, i=0; val<fMax+fStep; i++, val=fMin+fStep*i){\r\n                ////for (let val=factor*attrValueOfMin, i=0; val<factor*attrValueOfMax+factor*attrValueOfStep; i++, val=factor*attrValueOfMin+factor*attrValueOfStep*i){\r\n                    let attributeValue = val / factor;\r\n                    let optionLabel;\r\n                    if (self.ctx.widgetConfig.decimals && self.ctx.widgetConfig.decimals>=0) {\r\n                        optionLabel = attributeValue.toFixed(self.ctx.widgetConfig.decimals);\r\n                    } else {\r\n                        optionLabel = attributeValue.toString();\r\n                    }\r\n                    if (attrValueOfUnit && attrValueOfUnit.length) {\r\n                        optionLabel = optionLabel + \" \" + attrValueOfUnit;\r\n                    }\r\n                    let item = {\r\n                        \"optionLabel\" : optionLabel,\r\n                        \"attributeValue\" : String(attributeValue)\r\n                    };\r\n                    self.ctx.$scope.options.push(item);\r\n                }\r\n            }\r\n            \r\n        }\r\n        \r\n        if (attrValue) {\r\n            self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = String(attrValue);\r\n            //refreshTextColor();\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n        \r\n        //console.log(\"onDataUpdatedForSubscribe():\", self.ctx.$scope.currentValue, self.ctx.$scope.originalValue, attrValue, apply);\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\"onDataUpdateErrorForSubscribe(): errorText=\", errorText);\r\n    }\r\n    function subscribeAttributesOrTimeseries(type, entityType, entityId /*, retrieveMethod, key*/) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        ////if (retrieveMethod == 'attribute') {\r\n            ////valueSubscriptionInfo[0].attributes = [ {name: key}];\r\n            valueSubscriptionInfo[0].attributes = [];\r\n            if (retrieveAttributeKey && retrieveAttributeKey.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKey } );\r\n            }\r\n            if (retrieveAttributeKeyOfMinValue && retrieveAttributeKeyOfMinValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfMinValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfMaxValue && retrieveAttributeKeyOfMaxValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfMaxValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfStepValue && retrieveAttributeKeyOfStepValue.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfStepValue } );\r\n            }\r\n            if (retrieveAttributeKeyOfUnit && retrieveAttributeKeyOfUnit.length) {\r\n                valueSubscriptionInfo[0].attributes.push( { name: retrieveAttributeKeyOfUnit } );\r\n            }\r\n        ////} else {\r\n        ////    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        ////}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) { \r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,                             // attribute name,\r\n            \"value\": convertValueFunction(value)    // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                self.ctx.$scope.error = \"\";\r\n            },\r\n            function fail(rejection) {\r\n                var errorText = rejection.status + \": \" + rejection.statusText;\r\n                if (self.ctx.settings.showError) {\r\n                    self.ctx.$scope.error =errorText\r\n                        \r\n                }\r\n                console.log(\"updateAttributes(): errorText=\", errorText);\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: convertValueFunction(value)\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                    retrieveValue();    //$scope.originalValue = $scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    /*if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }*/\r\n                var errorText = $translate.instant('widgets.input-widgets.update-failed');\r\n                self.ctx.$scope.error = errorText;\r\n                console.log(\"updateTimeseries(): errorText=\", errorText);\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    /*function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = responseBody;\r\n                    self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = parseValueFunction(responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error = \"retrieveRPCMethod is null.\";\r\n        }\r\n    }*/\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, convertValueFunction(value), timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                console.log(\"rpcUpdateValue(): errorText=\", self.ctx.$scope.error);\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n    self.ctx.$scope.updateValue = function() {\r\n        //refreshTextColor();\r\n        \r\n        var newValue = self.ctx.$scope.currentValue * 1;\r\n        console.log(\"updateValue():\", self.ctx.$scope.currentValue, newValue);\r\n\r\n        if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n            updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateMethod, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"timeseries\") {\r\n            updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                self.ctx.defaultSubscription.targetDeviceId, \r\n                settings.updateAttributeKey, \r\n                newValue);\r\n                \r\n        } else if (settings.updateMethod === \"rpc\") {\r\n            rpcUpdateValue(settings.updateRPCMethod, \r\n                newValue, \r\n                settings.requestTimeout);\r\n            \r\n        } else {\r\n            self.ctx.$scope.error = \"updateMethod is error!\";\r\n\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                ////if (settings.retrieveMethod == 'rpc') {\r\n                ////    rpcRetrieveValue(settings.retrieveRPCMethod, settings.requestTimeout);\r\n                ////} else if (settings.retrieveMethod == 'attribute' || settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributesOrTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId/*, \r\n                            settings.retrieveMethod, \r\n                            settings.retrieveAttributeKey*/);\r\n                    }\r\n                ////}\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n\r\n\r\n/*\"retrieveMethod\": {\r\n    \"title\": \"Retrieve double value using method\",\r\n    \"type\": \"string\",\r\n    \"default\": \"rpc\"\r\n},\r\n\"retrieveAttributeKey\": {\r\n    \"title\": \"Retrieve Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\r\n    \"type\": \"string\",\r\n    \"default\": \"\"\r\n},\r\n\"retrieveRPCMethod\": {\r\n    \"title\": \"Retrieve value using RPC method (only when call RPC get value method)\",\r\n    \"type\": \"string\",\r\n    \"default\": \"\"\r\n},\r\n\"parseValueFunction\": {\r\n    \"title\": \"Parse value function, f(data), returns double value\",\r\n    \"type\": \"string\",\r\n    \"default\": \"return data; \"\r\n},\r\n----------------------------------------------------------\r\n{\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call RPC get value method\"\r\n                }\r\n            ]\r\n        },\r\n        \"retrieveAttributeKey\",\r\n        \"retrieveRPCMethod\",\r\n        {\r\n            \"key\": \"parseValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n\r\n*/\r\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"retrieveAttributeKey\": {\n                \"title\": \"Device attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfMinValue\": {\n                \"title\": \"Device attribute key of Min value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfMaxValue\": {\n                \"title\": \"Device attribute key of Max value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfStepValue\": {\n                \"title\": \"Device attribute key of Step value\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrieveAttributeKeyOfUnit\": {\n                \"title\": \"Device attribute key of Unit\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \n            \"updateMethod\": {\n                \"title\": \"Update double value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"updateAttributeKey\": {\n                \"title\": \"Update Attribute/Timeseries value key (only when update for attribute/timeseries method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"updateRPCMethod\": {\n                \"title\": \"Update value using RPC method (only when call RPC set value method)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"convertValueFunction\": {\n                \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n                \"type\": \"string\",\n                \"default\": \"return value; /* console.log(value); */\"\n            },\n\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n\n            \"required\": [\n                \"retrieveAttributeKey\",\n                \"retrieveAttributeKeyOfMinValue\",\n                \"retrieveAttributeKeyOfMaxValue\",\n                \"retrieveAttributeKeyOfStepValue\",\n                \"updateMethod\"\n            ]\n        }\n    },\n    \"form\": [\n        \"title\",\n\n        \"retrieveAttributeKey\",\n        \"retrieveAttributeKeyOfMinValue\",\n        \"retrieveAttributeKeyOfMaxValue\",\n        \"retrieveAttributeKeyOfStepValue\",\n        \"retrieveAttributeKeyOfUnit\",\n        \n        {\n            \"key\": \"updateMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [\n                {\n                    \"value\": \"SHARED_SCOPE\",\n                    \"label\": \"Update shared attribute\"\n                },\n                {\n                    \"value\": \"SERVER_SCOPE\",\n                    \"label\": \"Update server attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Update timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call RPC set value method\"\n                }\n            ]\n        },\n        \"updateAttributeKey\",\n        \"updateRPCMethod\",\n        {\n            \"key\": \"convertValueFunction\",\n            \"type\": \"javascript\"\n        },\n        \n        \"requestTimeout\"\n    ]\n}",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"optionStyle\":{\"orientation\":\"column\",\"isRaised\":true,\"checkedOption\":{\"isPrimary\":true},\"uncheckedOption\":{\"isPrimary\":false}},\"options\":[{\"optionLabel\":\"Option1 Label\",\"attributeValue\":\"option1\"},{\"optionLabel\":\"Option2 Label\",\"attributeValue\":\"option2\"}],\"retrieveMethod\":\"rpc\",\"retrieveAttributeKey\":\"value\",\"retrieveRPCMethod\":\"getValue\",\"parseValueFunction\":\"/* console.log(data); */\\nreturn data;\",\"updateMethod\":\"rpc\",\"updateAttributeKey\":\"value\",\"updateRPCMethod\":\"setValue\",\"convertValueFunction\":\"/* console.log(value); */\\nreturn value;\",\"requestTimeout\":5000},\"title\":\"Select double value from flexiable options\",\"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\":{},\"targetDeviceAliases\":[]}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.update_time_value_with_pattern_key",
      "name": "Update time value with pattern key",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 8,
        "sizeY": 2.5,
        "resources": [],
        "templateHtml": "<div tb-toast toastTarget=\"{{ toastTargetId }}\"\r\n    style=\"width: 100%; height: 100%;\">\r\n\r\n    <div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n        <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n            class=\"title-container\" fxLayout=\"row\"\r\n            fxLayoutAlign=\"start center\"\r\n            [fxShow]=\"showTitle\">\r\n            <span class=\"button-title\">{{title}}</span>\r\n        </div>\r\n\r\n        <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n            [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n            class=\"button-container\" fxLayout=\"column\"\r\n            fxLayoutAlign=\"center center\">\r\n\r\n            <form class=\"attribute-update-form\"\r\n                name=\"attrUpdateForm\"\r\n                (ngSubmit)=\"updateValue()\"\r\n                [fxShow]=\"entityDetected && isValidParameter && dataKeyDetected\">\r\n\r\n                <mat-form-field class=\"mat-block\"\r\n                    style=\"height: 26px;\">\r\n                    <input mat-timepicker matInput\r\n                        [(ngModel)]=\"currentValue\" required\r\n                        type=\"time\"\r\n                        [ngModelOptions]=\"{standalone: true}\" />\r\n                    <mat-error>\r\n                        {{requiredErrorMessage}}\r\n                    </mat-error>\r\n                </mat-form-field>\r\n\r\n                <button mat-icon-button\r\n                    class=\"applyChanges\" type=\"submit\"\r\n                    [disabled]=\"(originalValue === currentValue) || (!currentValue)\"\r\n                    matTooltip=\"{{ 'widgets.input-widgets.update-attribute' | translate }}\"\r\n                    matTooltipPosition=\"above\">\r\n                    <mat-icon>check</mat-icon>\r\n                </button>\r\n                <button mat-icon-button\r\n                    class=\"discardChanges\" type=\"button\"\r\n                    [disabled]=\"originalValue === currentValue\"\r\n                    (click)=\"currentValue = originalValue;\"\r\n                    matTooltip=\"{{ 'widgets.input-widgets.discard-changes' | translate }}\"\r\n                    matTooltipPosition=\"above\">\r\n                    <mat-icon>close</mat-icon>\r\n                </button>\r\n\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxHide]=\"entityDetected\"\r\n                    [innerHtml]=\"message\"></div>\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxShow]=\"entityDetected && !dataKeyDetected\">\r\n                    {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n                </div>\r\n                <div style=\"text-align: center; font-size: 18px; color: #a0a0a0;\"\r\n                    [fxShow]=\"entityDetected && !isValidParameter\">\r\n                    {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n                </div>\r\n\r\n            </form>\r\n\r\n        </div>\r\n    </div>\r\n\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container input {\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.5;\r\n    color: #333333;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\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.tb-rpc-button .attribute-update-form {\r\n    overflow: hidden;\r\n    width: 100%;\r\n    height: 100%;\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: center;\r\n    align-items: center;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form .mat-button.mat-icon-button {\r\n    margin: 0;\r\n    width: 24px;\r\n    min-width: 24px;\r\n    height: 24px;\r\n    min-height: 24px;\r\n    padding: 0 !important;\r\n    margin: 0 !important;\r\n    line-height: 20px;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form .mat-icon-button mat-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.tb-rpc-button .attribute-update-form mdp-date-picker,\r\n.tb-rpc-button .attribute-update-form mdp-time-picker {\r\n    width: 100%;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form mdp-date-picker md-input-container,\r\n.tb-rpc-button .attribute-update-form mdp-time-picker md-input-container \r\n{\r\n    margin: 5px 0 5px;\r\n    width: 100%;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form grid__element .button.mat-icon-button\r\n{\r\n    margin: 2px 0 0;\r\n    padding: 0;\r\n    width: 22px;\r\n    height: 20px;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form mat-form-field .mat-form-field-infix \r\n{\r\n    padding: 0;\r\n    border-top: .6em solid transparent;\r\n    width: fit-content;\r\n}\r\n\r\n.tb-rpc-button .attribute-update-form.small-width mdp-date-picker md-input-container,\r\n.tb-rpc-button .attribute-update-form.small-width mdp-time-picker md-input-container {\r\n    width: 78px;\r\n}\r\n\r\n.tb-rpc-button .show-label label {\r\n    display: block;\r\n}\r\n\r\nlabel {\r\n    display: none;\r\n}\r\n\r\nmd-toast{\r\n    min-width: 0;\r\n}\r\n\r\n.tb-toast {\r\n    font-size: 14px!important;\r\n}\r\n",
        "controllerScript": "// v1.0.0, 2023-05-25 13:47\r\n\r\n// placeholder=\"{{ 'widgets.input-widgets.time' | translate }}\"\r\n\r\nlet valueSubscription;\r\nlet $scope;\r\n\r\nself.onInit = function() {\r\n    //console.log(\"self.onInit()\");\r\n    self.ctx.ngZone.run(function() {\r\n       init();\r\n       self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\n\r\nfunction init() {\r\n\r\n    $scope = self.ctx.$scope;\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    $scope.settings = settings;\r\n    $scope.isValidParameter = true;                 // yes,html\r\n    $scope.entityDetected = true;       // false;   // yes,html\r\n    $scope.dataKeyDetected = true;      // false;   // yes,html\r\n    $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');    // yes,html\r\n    $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant            ('widgets.input-widgets.entity-attribute-required');    // yes,html\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    //self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n    self.ctx.widgetTitle = $scope.titleTemplate;\r\n\r\n    self.ctx.$scope.showTitle = self.ctx.settings.title && self.ctx.settings.title.length ? true : false;\r\n    self.ctx.$scope.title = self.ctx.settings.title;\r\n\r\n    let stateParams = {};\r\n    if (!self.ctx.isEdit && self.ctx.stateController) {\r\n        stateParams = self.ctx.stateController.getStateParams();\r\n    }\r\n    let patternCommonParam = settings.patternCommonParam;\r\n\r\n    /*function parseValue(data) {\r\n        //Using retrieve data: parse \"09:30\" to \"Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)\"\r\n        let result;\r\n        if (data && data.length>1 && typeof data === \"string\") {\r\n            var strarr = data.split(':', 3);\r\n            if (strarr && strarr.length>=2) {\r\n                var hour = Number(strarr[0]);\r\n                var min = Number(strarr[1]);\r\n                \r\n                if (hour>=0 && hour<=23 && min>=0 && min<=59) {\r\n                    var m = moment(\"1970-01-01 00:00:00.000\")\r\n                    m.hour(hour); \r\n                    m.minute(min); \r\n                    if (m.isValid()) {\r\n                        result = m.toDate();\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        console.log(\"parseValue(): \", data, result);\r\n        return result;\r\n    }*/\r\n\r\n    let patternParamOfetrievedAttributeKeyOrMethodName = \" \";\r\n    if (settings.retrieveMethod === \"attribute\" || settings.retrieveMethod === \"SHARED_SCOPE\" || settings.retrieveMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveAttributeKey;\r\n    } else if (settings.retrieveMethod === \"timeseries\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveTimeseriesKey;\r\n    } else if (settings.retrieveMethod === \"rpc\") {\r\n        patternParamOfetrievedAttributeKeyOrMethodName = settings.patternParamOfRetrieveRPCMethodName;\r\n    }\r\n\r\n    let convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertRetrievedAttributeKeyOrMethodNameFunction && settings.convertRetrievedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfetrievedAttributeKeyOrMethodName', 'patternCommonParam', settings.convertRetrievedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertRetrievedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam) => patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let retrievedAttributeKeyOrMethodName = convertRetrievedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam);\r\n\r\n    let convertRetrievedValueFunction = (value) => value; //parseValue;  // Retrieve data\r\n    if (settings.convertRetrievedValueFunction && settings.convertRetrievedValueFunction.length) { \r\n        try {\r\n            convertRetrievedValueFunction = new Function('value', settings.convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = (value) => value; //parseValue;\r\n        }\r\n    }\r\n\r\n    /*function prefixInteger(num, n) {\r\n        return (Array(n).join(0) + num).slice(-n);\r\n    }    \r\n    function convertValue(value) {\r\n        // Using update data: convert 'Thu Jan 01 1970 09:30:00 GMT+0800 (香港標準時間)' to \"09:30\"\r\n        let result;\r\n        var m = moment(value);\r\n        if (m.isValid()) {\r\n            //result = prefixInteger(m.hour(), 2) + \":\" +　prefixInteger(m.minute(), 2);\r\n            result = (Array(2).join(0) + m.hour()).slice(-2) + \":\" +　(Array(2).join(0) + m.minute()).slice(-2);\r\n        }\r\n        return result;\r\n    }*/\r\n    \r\n    let patternParamOfUpdateAttributeKeyOrMethodName = \" \";\r\n    if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateAttributeKey;\r\n    } else if (settings.updateMethod === \"timeseries\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateTimeseriesKey;\r\n    } else if (settings.updateMethod === \"rpc\") {\r\n        patternParamOfUpdateAttributeKeyOrMethodName = settings.patternParamOfUpdateRPCMethodName;\r\n    }\r\n\r\n    let convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n    if (settings.convertUpdatedAttributeKeyOrMethodNameFunction && settings.convertUpdatedAttributeKeyOrMethodNameFunction.length) {\r\n        try {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = new Function('stateParams', 'patternParamOfUpdateAttributeKeyOrMethodName', 'patternCommonParam', settings.convertUpdatedAttributeKeyOrMethodNameFunction);\r\n        } catch (e) {\r\n            convertUpdatedAttributeKeyOrMethodNameFunction = (stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam) => patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam;\r\n        }\r\n    }\r\n    let updateAttributeKeyOrMethodName = convertUpdatedAttributeKeyOrMethodNameFunction(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam);\r\n    \r\n    let convertUpdatedValueFunction = (value) => value; //convertValue;    // Update data\r\n    if (settings.convertUpdatedValueFunction && settings.convertUpdatedValueFunction.length) {\r\n        try {\r\n            convertUpdatedValueFunction = new Function('value', settings.convertUpdatedValueFunction);\r\n        } catch (e) {\r\n            convertUpdatedValueFunction = (value) => value; //convertValue;\r\n        }\r\n    }\r\n\r\n    // subscribe attribute & timeseries\r\n    function onDataUpdatedForSubscribe(subscription, apply) {\r\n        if (subscription.data.length) {\r\n            var keyData = subscription.data[0];\r\n            if (keyData && keyData.data && keyData.data[0]) {\r\n                var attrValue = keyData.data[0][1];\r\n                if (attrValue) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(attrValue);  ////angular.fromJson(attrValue)\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        \r\n        if (apply) {\r\n            self.ctx.detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n    function onDataUpdateErrorForSubscribe(subscription, e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n    }\r\n    \r\n    /*function subscribeAttributesOrTimeseries(type, entityType, entityId, retrieveMethod, key) {\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        } else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        }\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }*/\r\n    \r\n    function subscribeAttributes(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n            valueSubscriptionInfo[0].attributes = [\r\n                {name: key}\r\n            ];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [\r\n        //        {name: key}\r\n        //    ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    function subscribeTimeseries(type, entityType, entityId, key) { //retrieveMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type:       type,\r\n            entityType: entityType,\r\n            entityId:   entityId\r\n        }];\r\n        \r\n        //if (retrieveMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [\r\n        //        {name: key}\r\n        //    ];\r\n        //} else {\r\n            valueSubscriptionInfo[0].timeseries = [\r\n                {name: key}\r\n            ];\r\n        //}\r\n        \r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo (\r\n            'latest', valueSubscriptionInfo, subscriptionOptions, false, true).subscribe( //types.widgetType.latest.value\r\n            (subscription) => {\r\n                valueSubscription = subscription;\r\n            }\r\n        );    \r\n    }\r\n    \r\n    // update attribute\r\n    function updateAttributes(entityType, entityId, attributeScope, key, value) {\r\n        let attributes = [];\r\n        attributes.push({\r\n            \"key\": key,         // attribute name,\r\n            \"value\": value      // attribute value\r\n        });\r\n\r\n        ////attributeService.saveEntityAttributes(self.ctx.datasources[0].entityType, self.ctx.datasources[0].entityId,\r\n        attributeService.saveEntityAttributes({\"entityType\":entityType, \"id\":entityId}, \r\n            attributeScope, attributes).subscribe(\r\n            function success() {\r\n                ////self.ctx.$scope.error = \"\";\r\n                if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n                retrieveValue();  //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n            },\r\n            function fail(rejection) {\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = rejection.status + \": \" + rejection.statusText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n    \r\n    // update timeseries \r\n    function saveEntityTimeseries(entityType, entityId, telemetries) {\r\n        var telemetriesData = {};\r\n        for (var a = 0; a < telemetries.length; a++) {\r\n            if (typeof telemetries[a].value !== 'undefined' && 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            return http.post(url, telemetriesData);\r\n        }\r\n        return null;\r\n    }\r\n    function updateTimeseries(entityType, entityId, key, value) {\r\n        //var datasource = self.ctx.datasources[0];\r\n        let observable = saveEntityTimeseries(\r\n            entityType,     // datasource.entityType,\r\n            entityId,       // datasource.entityId,\r\n            [\r\n                {\r\n                    key: key,\r\n                    value: value\r\n                }\r\n            ]\r\n        );\r\n        if (observable) {\r\n            observable.subscribe(\r\n                function success() {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue();    //self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                },\r\n                function fail() {\r\n                    ////if (self.ctx.settings.showError)\r\n                    ////    self.ctx.$scope.error = $translate.instant('widgets.input-widgets.update-failed');\r\n                    ////}\r\n                    if (settings.showResultMessage) {\r\n                        $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                }\r\n            );\r\n        }\r\n    }\r\n\r\n    // RPC retrieve value\r\n    function rpcRetrieveValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method, null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    try {\r\n                        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = convertRetrievedValueFunction(responseBody);\r\n                        ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                    } catch (e){\r\n                        //self.ctx.$scope.error = 'convertRetrievedValueFunction(value) error!'\r\n                        $scope.showErrorToast(translate.instant(e), 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                },\r\n                function fail() {\r\n                    ////self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n\t\t    $scope.showErrorToast(translate.instant(self.ctx.defaultSubscription.rpcErrorText), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            );\r\n        } else {\r\n            ////self.ctx.$scope.error = \"retrievedAttributeKeyOrMethodName is null.\";\r\n            $scope.showErrorToast(translate.instant('retrievedAttributeKeyOrMethodName is null.'), 'bottom', 'left', $scope.toastTargetId);\r\n        }\r\n    }\r\n    // RPC update value\r\n    function rpcUpdateValue(method, value, timeout) {\r\n        if (rpcUpdateStatus.executingUpdateValue) {\r\n            rpcUpdateStatus.scheduledValue = value;\r\n            return;\r\n        } else {\r\n            rpcUpdateStatus.scheduledValue = null;\r\n            rpcUpdateStatus.rpcValue = value;\r\n            rpcUpdateStatus.executingUpdateValue = true;\r\n        }\r\n        self.ctx.$scope.error = '';\r\n        self.ctx.controlApi.sendOneWayCommand(method, value, timeout).subscribe(\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                if (rpcUpdateStatus.scheduledValue !== null && rpcUpdateStatus.scheduledValue != rpcUpdateStatus.rpcValue) {\r\n                    rpcUpdateValue(rpcUpdateStatus.scheduledValue);\r\n                } else {\r\n                    ////self.ctx.$scope.error = \"\";\r\n                    if (settings.showResultMessage) {\r\n\t\t\t$scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n                    }\r\n                    retrieveValue(); // self.ctx.$scope.originalValue = self.ctx.$scope.currentValue = value;\r\n                }\r\n            },\r\n            () => {\r\n                rpcUpdateStatus.executingUpdateValue = false;\r\n                ////if (self.ctx.settings.showError) {\r\n                ////    self.ctx.$scope.error = self.ctx.defaultSubscription.rpcErrorText;\r\n                ////}\r\n                if (settings.showResultMessage) {\r\n\t\t    $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n                }\r\n            }\r\n        );\r\n    }\r\n\r\n    // update value\r\n\r\n    self.ctx.$scope.updateValue = function(option) {\r\n        var newValue = convertUpdatedValueFunction($scope.currentValue);\r\n        //console.log(\"updateValue():\", newValue);\r\n        \r\n        if (newValue && newValue.length){\r\n            if (settings.updateMethod === \"SHARED_SCOPE\" || settings.updateMethod === \"SERVER_SCOPE\") {\r\n                updateAttributes(\"DEVICE\", //types.entityType.device, \r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    settings.updateMethod, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateAttributeKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"timeseries\") {\r\n                updateTimeseries(\"DEVICE\", // types.entityType.device,  types.constant.js\r\n                    self.ctx.defaultSubscription.targetDeviceId, \r\n                    updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateTimeseriesKey, \r\n                    newValue);\r\n                    \r\n            } else if (settings.updateMethod === \"rpc\") {\r\n                rpcUpdateValue(updateAttributeKeyOrMethodName, //settings.patternParamOfUpdateRPCMethodName, \r\n                    newValue, \r\n                    settings.requestTimeout);\r\n                \r\n            } else {\r\n                self.ctx.$scope.error = \"updateMethod is error!\";\r\n    \r\n            }\r\n        }\r\n    }\r\n\r\n    // retrieve value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error = 'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrieveMethod == 'rpc') {\r\n                    rpcRetrieveValue(retrievedAttributeKeyOrMethodName, settings.requestTimeout); //settings.patternParamOfRetrieveRPCMethodName\r\n                } else if (settings.retrieveMethod == 'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveAttributeKey\r\n                    }\r\n                } else if (settings.retrieveMethod == 'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription.targetDeviceId, \r\n                            retrievedAttributeKeyOrMethodName); //settings.patternParamOfRetrieveTimeseriesKey\r\n                    }\r\n                }\r\n            } \r\n        }\r\n        \r\n        firstRetrieveValue = false;\r\n    }\r\n    \r\n    retrieveValue();\r\n};\r\n\r\nself.onResize = function() {\r\n    $scope.smallWidthContainer = (self.ctx.$container[0].offsetWidth < 320) ? true : false;\r\n    $scope.changeAlignment = false;\r\n};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.currentValue = self.ctx.$scope.originalValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"patternCommonParam\": {\r\n                \"title\": \"Pattern common param\",\r\n                \"type\": \"string\",\r\n                \"default\": \"0\"\r\n            },\r\n\r\n            \"retrieveMethod\": {\r\n                \"title\": \"Retrieve string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfRetrieveAttributeKey\": {\r\n                \"title\": \"Pattern param of retrieved Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveTimeseriesKey\": {\r\n                \"title\": \"Pattern param of retrieved Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfRetrieveRPCMethodName\": {\r\n                \"title\": \"Pattern param of retrieved value using server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n                \"convertRetrievedAttributeKeyOrMethodNameFunction\": {\r\n                    \"title\": \"Convert function, f(stateParams, patternParamOfetrievedAttributeKeyOrMethodName, patternCommonParam), returns retireved attribute key, time-series data key or server-side RPC method name\",\r\n                    \"type\": \"string\",\r\n                    \"default\": \"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n                },\r\n            \"convertRetrievedValueFunction\": {\r\n                \"title\": \"Parse value function, f(value), returns string\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n            \r\n            \"updateMethod\": {\r\n                \"title\": \"Update string value using method\",\r\n                \"type\": \"string\",\r\n                \"default\": \"rpc\"\r\n            },\r\n            \"patternParamOfUpdateAttributeKey\": {\r\n                \"title\": \"Pattern param of updated Attribute key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateTimeseriesKey\": {\r\n                \"title\": \"Pattern param of updated Timeseries key\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \"patternParamOfUpdateRPCMethodName\": {\r\n                \"title\": \"Pattern param of updated server-side RPC method name\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n                \"convertUpdatedAttributeKeyOrMethodNameFunction\": {\r\n                    \"title\": \"Convert function, f(stateParams, patternParamOfUpdateAttributeKeyOrMethodName, patternCommonParam), returns updated attribute key, time-series data key or server-side RPC method name\",\r\n                    \"type\": \"string\",\r\n                    \"default\": \"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\"\r\n                },\r\n            \"requestTimeout\": {\r\n                \"title\": \"RPC request timeout\",\r\n                \"type\": \"number\",\r\n                \"default\": 5000\r\n            },\r\n            \"convertUpdatedValueFunction\": {\r\n                \"title\": \"Convert function, f(value), returns updated value\",\r\n                \"type\": \"string\",\r\n                \"default\": \"return value;\"\r\n            },\r\n\r\n            \"showResultMessage\":{\r\n                \"title\":\"Show result message\",\r\n                \"type\":\"boolean\",\r\n                \"default\":true\r\n            },\r\n            \"requiredErrorMessage\": {\r\n                \"title\": \"'Required' error message\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n        \"required\": [\r\n            \"patternCommonParam\",\r\n            \"retrieveMethod\",\r\n            \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n            \"updateMethod\",\r\n            \"convertUpdatedAttributeKeyOrMethodNameFunction\"\r\n        ]\r\n    },\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"patternCommonParam\",\r\n\r\n        {\r\n            \"key\": \"retrieveMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"none\",\r\n                    \"label\": \"Don't retrieve\"\r\n                },\r\n                {\r\n                    \"value\": \"attribute\",\r\n                    \"label\": \"Subscribe for attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Subscribe for timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-dide RPC to get value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveAttributeKey\",\r\n            \"condition\": \"model.retrieveMethod === 'attribute' || model.retrieveMethod === 'SHARED_SCOPE' || model.retrieveMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveTimeseriesKey\",\r\n            \"condition\": \"model.retrieveMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfRetrieveRPCMethodName\",\r\n            \"condition\": \"model.retrieveMethod === 'rpc'\"\r\n        },\r\n            {\r\n                \"key\": \"convertRetrievedAttributeKeyOrMethodNameFunction\",\r\n                \"type\": \"javascript\"\r\n            },\r\n        {\r\n            \"key\": \"convertRetrievedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Retrieve the above attributes from device.</small></I>\"\r\n        },\r\n        \r\n        {\r\n            \"key\": \"updateMethod\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"SHARED_SCOPE\",\r\n                    \"label\": \"Update shared attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"SERVER_SCOPE\",\r\n                    \"label\": \"Update server attribute\"\r\n                },\r\n                {\r\n                    \"value\": \"timeseries\",\r\n                    \"label\": \"Update timeseries\"\r\n                },\r\n                {\r\n                    \"value\": \"rpc\",\r\n                    \"label\": \"Call server-side RPC to set value\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateAttributeKey\",\r\n            \"condition\": \"model.updateMethod === 'SHARED_SCOPE' || model.updateMethod === 'SERVER_SCOPE'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateTimeseriesKey\",\r\n            \"condition\": \"model.updateMethod === 'timeseries'\"\r\n        },\r\n        {\r\n            \"key\": \"patternParamOfUpdateRPCMethodName\",\r\n            \"condition\": \"model.updateMethod === 'rpc'\"\r\n        },\r\n            {\r\n                \"key\": \"convertUpdatedAttributeKeyOrMethodNameFunction\",\r\n                \"type\": \"javascript\"\r\n            },\r\n        {\r\n            \"key\": \"convertUpdatedValueFunction\",\r\n            \"type\": \"javascript\"\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"<I><small>Note: Call the above method and prameters to update value to device.</small></I>\"\r\n        },\r\n        \r\n        \"requestTimeout\",\r\n        \r\n        \"showResultMessage\",\r\n        \"requiredErrorMessage\"\r\n    ]\r\n}\r\n",
        "dataKeySettingsSchema": "{}\r\n",
        "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"retrieveMethod\":\"rpc\",\"updateMethod\":\"rpc\",\"requestTimeout\":5000,\"showResultMessage\":true,\"patternCommonParam\":\"0\",\"convertRetrievedAttributeKeyOrMethodNameFunction\":\"return patternParamOfetrievedAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\",\"convertRetrievedValueFunction\":\"return value;\",\"convertUpdatedAttributeKeyOrMethodNameFunction\":\"return patternParamOfUpdateAttributeKeyOrMethodName + patternCommonParam; /* console.log(value); */\",\"convertUpdatedValueFunction\":\"return value;\"},\"title\":\"Update time value with pattern key\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[],\"enableDataExport\":true,\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.tabs_navigation_bar",
      "name": "Tabs navigation bar",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 9.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"my-tbs-navigation\" fxFlex fxLayout=\"column\"\r\n    style=\"height: 100%;\"\r\n    fxLayoutAlign=\"space-around stretch\">\r\n\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"center center\">\r\n\r\n        <mat-tab-group mat-stretch-tabs \r\n             [headerPosition]=\"position\" [attr.mat-align-tabs]=\"align\"\r\n             [color]=\"color\" [backgroundColor]=\"bgColor\"\r\n            animationDuration=\"0ms\"\r\n            (selectedTabChange)=\"onSelectedTabChange($event)\"\r\n            [selectedIndex]=\"tabsSelected\">\r\n            <mat-tab *ngFor=\"let tab of tabs\">\r\n                <ng-template mat-tab-label>\r\n                    <div *ngIf=\"tab.icon\">\r\n                        <mat-icon class=\"example-tab-icon\">\r\n                            {{tab.icon}}</mat-icon>\r\n                    </div>{{tab.name}}\r\n                </ng-template>\r\n            </mat-tab>\r\n        </mat-tab-group>\r\n    </div>\r\n\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".my-tbs-navigation {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.my-tbs-navigation .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    /*margin: 10px;*/\r\n    margin: 0px;\r\n}\r\n\r\n.my-tbs-navigation .button-container{\r\n    /*min-width: 80%;*/\r\n    min-width: 40%;\r\n}\r\n\r\n/*.my-tbs-navigation .button-container .mat-tab-group {*/\r\n/*    width: 100%;*/\r\n/*}*/\r\n\r\n.my-tbs-navigation .button-container .mat-tab-label {\r\n    padding: 0 2px;\r\n    /*min-width: 80px*/\r\n}\r\n\r\n.my-tbs-navigation .button-container .example-tab-icon{\r\n  margin-right: 0px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-button{\r\n    /*width: 100%;*/\r\n    margin: 0;\r\n    padding: 0 0px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .material-icons, .material-icons-round {\r\n    /*font-size: 24px;*/\r\n    font-size: 16px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-icon {\r\n    /*height: 24px;*/\r\n    /*width: 24px;*/\r\n    height: 16px;\r\n    width: 16px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mat-mdc-tab-header .mdc-tab {\r\n    /*height: 48px;*/\r\n    height: 20px;\r\n}\r\n\r\n.my-tbs-navigation .button-container .mdc-tab {\r\n    /*min-width: 90px;*/\r\n    /*padding-right: 24px;*/\r\n    /*padding-left: 24px;*/\r\n    min-width: 50px;\r\n    padding-right: 4px;\r\n    padding-left: 4px;\r\n}\r\n\r\n.my-tbs-navigation .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.my-tbs-navigation .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n",
        "controllerScript": "// v.2.4, 2023-09-25 17:00\r\n\r\nself.onInit = function() {\r\n    self.ctx.$scope.onSelectedTabChange = function(event) {\r\n        //console.log(\"self.ctx\", self.ctx);\r\n        //console.log(\"event=\", event);\r\n        \r\n        //console.log(\"self.ctx.$scope.tabsSelected=\", self.ctx.$scope.tabsSelected);\r\n        //console.log(\"event.index=\", event.index);\r\n        \r\n        if (self.ctx.$scope.tabsSelected == event.index) {\r\n            return;\r\n        } else {\r\n            self.ctx.$scope.tabsSelected = event.index;\r\n        }\r\n        \r\n        let entityInfo = self.ctx.actionsApi.getActiveEntityInfo();\r\n        //entityInfo.entityId, entityInfo.entityLabel, entityInfo.entityName\r\n        //console.log(entityInfo);\r\n        \r\n        const id_ = entityInfo ? entityInfo.id : null\r\n            , entityType_ = entityInfo ? entityInfo.entityType : null\r\n            , entityId = {entityType: entityType_, id: id_}\r\n            , entityName = entityInfo ? entityInfo.entityName : null\r\n            , entityLabel = entityInfo ? entityInfo.entityLabel : null;\r\n\r\n        if (!self.ctx.isEdit && (event.index<self.ctx.$scope.tabs.length)) //entityInfo &&\r\n        {\r\n            const descriptor = self.ctx.$scope.tabs[event.index];\r\n            //console.log(\"descriptor=\", descriptor);\r\n            if (descriptor) {\r\n                //event.stopPropagation();\r\n                // self.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName);\r\n                self.ctx.actionsApi.handleWidgetAction(event, descriptor,\r\n                    entityId, entityName, null, entityLabel);\r\n            }\r\n        }\r\n    };\r\n\r\n    ////self.ctx.ngZone.run(function() {\r\n       init();\r\n    //  self.ctx.detectChanges(true);\r\n    //});\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    //let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    //let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    //let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    //let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.position = settings.position;\r\n    self.ctx.$scope.align = settings.align;\r\n    //self.ctx.$scope.minWidth = settings.minWidth;\r\n    //self.ctx.$scope.isPrimary = settings.isPrimary; //\r\n    self.ctx.$scope.color = settings.color; //\r\n    self.ctx.$scope.bgColor = settings.bgColor;\r\n    self.ctx.$scope.tabs = self.ctx.actionsApi.getActionDescriptors(\"elementClick\");  ////actionDescriptors = ////settings.tabs;\r\n    self.ctx.$scope.tabsSelected = settings.tabsSelected;\r\n    if (self.ctx.$scope.tabsSelected<0) {\r\n        self.ctx.$scope.tabsSelected = 0;\r\n    } else if (self.ctx.$scope.tabsSelected>=self.ctx.$scope.tabs.length) {\r\n        self.ctx.$scope.tabsSelected = self.ctx.$scope.tabs.length-1;\r\n    }\r\n    \r\n    // console.log(\"actionDescriptors=\", self.ctx.$scope.tabs);\r\n\r\n    /*self.ctx.$scope.tabGroupStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.isPrimary === false) {\r\n        self.ctx.$scope.tabGroupStyle['background-color']  = self.ctx.$scope.bgColor;\r\n        self.ctx.$scope.tabGroupStyle['color']             = self.ctx.$scope.color;\r\n    }*/\r\n\r\n    //console.log(\"onInit\", self.ctx.$scope.align, self.ctx.$scope.position);\r\n}\r\n\r\nself.onResize = function() {\r\n    //    min-width: 80%\r\n};\r\n\r\nself.actionSources = function() {\r\n    return {\r\n        'elementClick': {\r\n            name: 'widget-action.element-click',\r\n            multiple: true\r\n        }\r\n    };\r\n};\r\n\r\nself.onDestory = function() {\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"tabsSelected\": {\r\n                \"title\": \"Tabs selected\",\r\n                \"type\": \"integer\",\r\n                \"default\": \"0\"\r\n            },\r\n            \"position\": {\r\n                \"title\": \"Text position\",\r\n                \"type\": \"string\",\r\n                \"default\": \"above\"\r\n            },\r\n            \"align\": {\r\n                \"title\": \"Align\",\r\n                \"type\": \"string\",\r\n                \"default\": \"center\"\r\n            },\r\n            \"color\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Tabs color\",\r\n                \"default\": \"\"\r\n            },\r\n            \"bgColor\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Tabs background color\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n            \r\n        \"required\": [\r\n            \"tabsSelected\",\r\n            \"position\",\r\n            \"align\"\r\n        ]\r\n    },\r\n\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"tabsSelected\",\r\n        {\r\n            \"key\": \"position\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"above\",\r\n                    \"label\": \"above\"\r\n                },\r\n                {\r\n                    \"value\": \"below\",\r\n                    \"label\": \"below\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"align\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"start\",\r\n                    \"label\": \"start\"\r\n                },\r\n                {\r\n                    \"value\": \"center\",\r\n                    \"label\": \"center\"\r\n                },\r\n                {\r\n                    \"value\": \"end\",\r\n                    \"label\": \"end\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"key\": \"bgColor\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        },\r\n        {\r\n            \"key\": \"color\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#FFFFFF\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"position\":\"above\",\"align\":\"center\",\"tabsSelected\":0},\"title\":\"Tabs navigation bar\",\"actions\":{\"elementClick\":[{\"name\":\"AAAA\",\"icon\":\"now_widgets\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Tab label</b>: ' + 'AAAA';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"e6bd9a5b-b2fa-233a-f7f7-271e9c742194\"},{\"name\":\"BBBB\",\"icon\":\"extension\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Tab label</b>: ' + 'BBBB';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"2fc2648c-0071-5a04-437b-2a7ed2240a69\"},{\"name\":\"CCCC\",\"icon\":\"bedroom_baby\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Tab label</b>: ' + 'CCCC';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"c7644acb-0f1e-18ab-edf0-71b0204c62e9\"}]},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"enableDataExport\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.setting_list",
      "name": "Setting list",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 9,
        "sizeY": 3,
        "resources": [],
        "templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\r\n    <div fxFlex=\"{{showTitle ? '1.1em' : '0em'}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n    <div fxFlex\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '5px'}\"\r\n        class=\"button-container\" fxLayout=\"column\"\r\n        fxLayoutAlign=\"start center\">\r\n\r\n        <mat-selection-list #shoes [multiple]=\"false\"\r\n            (selectionChange)=\"listSelectionChange(shoes.selectedOptions.selected[0].value)\"\r\n            id=\"list_of_days_of_weeks\">\r\n            <mat-list-option\r\n                *ngFor=\"let item of settingList; index as index; last as last\"\r\n                [value]=\"index\"\r\n                [disabled]=\"isDsiabledListOption(index)\">\r\n                <div mat-line>\r\n                    <mat-icon mat-list-icon matListItemIcon>{{item.icon}}\r\n                    </mat-icon> &nbsp;\r\n                    <span class=\"setting-name\">{{item.text}}</span>\r\n                    <span> &gt; </span>\r\n                </div>\r\n                <mat-divider [inset]=\"true\" *ngIf=\"!last\">\r\n                </mat-divider>\r\n            </mat-list-option>\r\n        </mat-selection-list>\r\n\r\n    </div>\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".tb-rpc-button {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.tb-rpc-button .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container div{\r\n    min-width: 80%\r\n}\r\n\r\n/* TC 20221020 */\r\n.tb-rpc-button .button-container mat-action-list{\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.6;\r\n    color: #333333;\r\n    \r\n    width: 100%;\r\n}\r\n\r\n/* TC 20230523 */\r\n.tb-rpc-button .button-container mat-selection-list{\r\n    font-size: 1em;\r\n    font-weight: bold;\r\n    line-height: 1.6;\r\n    color: #333333;\r\n    \r\n    width: 100%;\r\n}\r\n\r\n/* TC 20230523 */\r\n.tb-rpc-button .mdc-list-item__end.ng-star-inserted {\r\n    visibility: hidden;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-list-base .mat-list-item.mat-list-item-with-avatar, .mat-list-base .mat-list-option.mat-list-item-with-avatar {\r\n    height: 2.4em;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-line\r\n{\r\n    display: flex;\r\n    flex-direction: row;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    width: 100%;\r\n    margin: 0;\r\n}\r\n\r\n.tb-rpc-button .button-container .mat-button{\r\n    padding: 0 0px;\r\n}\r\n\r\n.tb-rpc-button .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.tb-rpc-button .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n\r\n.tb-rpc-button .mdc-list-item--with-trailing-radio.mdc-list-item {\r\n    /*padding-right: 0;*/\r\n    padding-right: 16px;\r\n}\r\n\r\n.tb-rpc-button .mdc-list-item--with-trailing-radio .mdc-list-item__end {\r\n    /*margin-left: 24px;*/\r\n    /*margin-right: 8px;*/\r\n    margin-left: 0;\r\n    margin-right: 0;\r\n}\r\n\r\n.tb-rpc-button  .mat-mdc-list-option .mdc-radio {\r\n    /*padding: calc((40px - 20px) / 2);*/\r\n    /*padding: calc((var(--mdc-radio-state-layer-size, 40px) - 20px) / 2);*/\r\n    /*padding-top: ;*/\r\n    /*padding-right: ;*/\r\n    /*padding-bottom: ;*/\r\n    /*padding-left: ;*/\r\n    padding: 0;\r\n    /*width: 0;*/\r\n    padding-right: 0;\r\n    padding-left: 0;\r\n}\r\n\r\n.tb-rpc-button .mat-mdc-list-option .mdc-radio .mdc-radio__native-control {\r\n    width: 0;\r\n}\r\n\r\n.tb-rpc-button .setting-name{\r\n    width: 100%;   \r\n}\r\n\r\n.tb-rpc-button .mat-divider.mat-divider-inset {\r\n    /*margin-left: 80px;*/\r\n    margin-left: 10px;\r\n    margin-right: 10px;\r\n}\r\n\r\n.tb-rpc-button .mdc-list-item--disabled {\r\n    /*background-color: rgba(0,0,0,.1);*/\r\n    opacity: 0.2;\r\n}\r\n",
        "controllerScript": "// v.2.4, 2023-09-25 17:00\r\n\r\n// Based on Segment switch of string value\r\n\r\nlet valueSubscription;\r\n\r\nself.onInit = function() {\r\n    self.ctx.ngZone.run(function() {\r\n        init();\r\n        self.ctx.detectChanges(true);\r\n    });\r\n};\r\n\r\nfunction init() {\r\n    let utils = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    let translate = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    let http = self.ctx.$scope.$injector.get(self.ctx\r\n        .servicesMap.get('http'));\r\n    let attributeService = self.ctx.$scope.$injector.get(\r\n        self.ctx.servicesMap.get('attributeService'));\r\n    let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {};\r\n\r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils\r\n        .guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings\r\n        .title.length ? true :\r\n        false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    self.ctx.$scope.convertedValue = 0;\r\n\r\n    // let stateParams = {};\r\n    // if (!self.ctx.isEdit && self.ctx.stateController) {\r\n    //     stateParams = self.ctx.stateController\r\n    //         .getStateParams();\r\n    // }\r\n    let widgetContext = self.ctx;\r\n\r\n    let convertRetrievedValueFunction = (retrievedValue) =>\r\n        retrievedValue; // Retrieved data\r\n    if (settings.convertRetrievedValueFunction && settings\r\n        .convertRetrievedValueFunction.length) {\r\n        try {\r\n            convertRetrievedValueFunction = new Function(\r\n                'retrievedValue', settings\r\n                .convertRetrievedValueFunction);\r\n        } catch (e) {\r\n            convertRetrievedValueFunction = (\r\n                retrievedValue) => retrievedValue;\r\n        }\r\n    }\r\n\r\n    //self.ctx.$scope.settingList = settings.settingList;\r\n    self.ctx.$scope.settingList = [];\r\n    for (const item of settings.settingList) {\r\n        self.ctx.$scope.settingList.push({\r\n            \"icon\": item.icon,\r\n            \"text\": item.text,\r\n            \"isDisabledFunc\": new Function(\r\n                'convertedValue', item\r\n                .isDisabledFunc),\r\n            \"customActionFunc\": new Function(\r\n                'convertedValue', 'widgetContext',\r\n                'openRightLayout', item\r\n                .customActionFunc)\r\n        });\r\n    }\r\n\r\n    let lastAttrValue = {}; //let lastAttrValue;\r\n    function onDataUpdatedForSubscribe(subscription,\r\n        apply) {\r\n        let convertedValue;\r\n\r\n        for (let i = 0; subscription.data && i <\r\n            subscription.data.length; i++) {\r\n            let keyName = subscription.data[i].dataKey.name;\r\n            if (subscription.data[i].data && subscription\r\n                .data[i].data.length > 0) {\r\n                let KeyValue = subscription.data[i].data[0][1];\r\n                //if (settings.retrievedAttributeKey && keyName === settings.retrievedAttributeKey) {\r\n                    try {\r\n                        convertedValue = convertRetrievedValueFunction(KeyValue); ////angular.fromJson(newValue)\r\n                    } catch (e) {\r\n                        self.ctx.$scope.error =\r\n                            'convertRetrievedValueFunction(value) error!'\r\n                    }\r\n                    //if (typeof KeyValue === 'number' && isFinite(KeyValue)) {\r\n                    //    convertedValue = KeyValue;\r\n                    //}\r\n                //} else {\r\n                //    console.log(\"unkown data:subscription.data[%d]\", i);\r\n                //    console.log(subscription.data[i]);\r\n                //}\r\n            }\r\n        }\r\n\r\n        if (convertedValue) {\r\n            self.ctx.$scope.convertedValue =\r\n                convertedValue; //String(convertedValue);\r\n            //refreshTextColor();\r\n        }\r\n\r\n        if (apply) {\r\n            self.ctx\r\n                .detectChanges(); //self.ctx.$scope.$digest();\r\n        }\r\n    }\r\n\r\n    function onDataUpdateErrorForSubscribe(subscription,\r\n        e) {\r\n        var exceptionData = utils.parseException(e);\r\n        var errorText = exceptionData.name;\r\n        if (exceptionData.message) {\r\n            errorText += ': ' + exceptionData.message;\r\n        }\r\n        self.ctx.$scope.error = errorText;\r\n        console.log(\r\n            \"onDataUpdateErrorForSubscribe(): errorText=\",\r\n            errorText);\r\n    }\r\n\r\n    function subscribeAttributes(type, entityType, entityId,\r\n        key) { //retrievedMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type: type,\r\n            entityType: entityType,\r\n            entityId: entityId\r\n        }];\r\n\r\n        //if (retrievedMethod == 'attribute') {\r\n        valueSubscriptionInfo[0].attributes = [{\r\n            name: key\r\n        }];\r\n        //} else {\r\n        //    valueSubscriptionInfo[0].timeseries = [{name: key}];\r\n        //}\r\n\r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo(\r\n                'latest', valueSubscriptionInfo,\r\n                subscriptionOptions, false, true)\r\n            .subscribe( //types.widgetType.latest.value\r\n                (subscription) => {\r\n                    valueSubscription = subscription;\r\n                }\r\n            );\r\n    }\r\n\r\n    function subscribeTimeseries(type, entityType, entityId,\r\n        key) { //retrievedMethod,\r\n        var valueSubscriptionInfo = [{\r\n            type: type,\r\n            entityType: entityType,\r\n            entityId: entityId\r\n        }];\r\n\r\n        //if (retrievedMethod == 'attribute') {\r\n        //    valueSubscriptionInfo[0].attributes = [{name: key}];\r\n        //} else {\r\n        valueSubscriptionInfo[0].timeseries = [{\r\n            name: key\r\n        }];\r\n        //}\r\n\r\n        var subscriptionOptions = {\r\n            callbacks: {\r\n                onDataUpdated: onDataUpdatedForSubscribe,\r\n                onDataUpdateError: onDataUpdateErrorForSubscribe\r\n            }\r\n        };\r\n        self.ctx.subscriptionApi.createSubscriptionFromInfo(\r\n                'latest', valueSubscriptionInfo,\r\n                subscriptionOptions, false, true)\r\n            .subscribe( //types.widgetType.latest.value\r\n                (subscription) => {\r\n                    valueSubscription = subscription;\r\n                }\r\n            );\r\n    }\r\n\r\n\r\n    // RPC retrieved value\r\n    function rpcRetrievedValue(method, timeout) {\r\n        if (method && method.length) {\r\n            self.ctx.$scope.error = '';\r\n            self.ctx.controlApi.sendTwoWayCommand(method,\r\n                null, timeout).subscribe(\r\n                function success(responseBody) {\r\n                    ////self.ctx.$scope.convertedValue = responseBody;\r\n                    self.ctx.$scope.convertedValue =\r\n                        convertRetrievedValueFunction(\r\n                            responseBody);\r\n\r\n                    ////self.ctx.$scope.$digest(); // Error: $digest already in progress\r\n                },\r\n                function fail() {\r\n                    self.ctx.$scope.error = self.ctx\r\n                        .defaultSubscription\r\n                        .rpcErrorText;\r\n                }\r\n            );\r\n        } else {\r\n            self.ctx.$scope.error =\r\n                \"retrievedRPCMethodName is null.\";\r\n        }\r\n    }\r\n\r\n    self.ctx.$scope.isDsiabledListOption = function(index) {\r\n        //console.log(\"isDsiabledListOption()\", index);\r\n        //if (self.ctx.$scope.settingList[index].isDisabledFunc) {\r\n        return self.ctx.$scope.settingList[index]\r\n            .isDisabledFunc(self.ctx.$scope\r\n                .convertedValue);\r\n        //}\r\n    }\r\n    self.ctx.$scope.listSelectionChange = function(index) {\r\n        //console.log(\"listSelectionChange()\", index);\r\n        self.ctx.$scope.settingList[index]\r\n            .customActionFunc(self.ctx.$scope\r\n                .convertedValue, widgetContext, false);\r\n    }\r\n\r\n    // retrieved value\r\n    var firstRetrieveValue = true;\r\n    function retrieveValue() {\r\n        let rpcEnabled = self.ctx.defaultSubscription\r\n            .rpcEnabled;\r\n        let isSimulated = self.ctx.$scope.widgetEditMode;\r\n        if (!rpcEnabled) {\r\n            self.ctx.$scope.error =\r\n                'Target device is not set!';\r\n        } else {\r\n            if (!isSimulated) {\r\n                if (settings.retrievedMethod == 'rpc') {\r\n                    rpcRetrievedValue(settings\r\n                        .retrievedRPCMethodName,\r\n                        settings.requestTimeout);\r\n                } else if (settings.retrievedMethod ==\r\n                    'attribute') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeAttributes(\r\n                            \"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription\r\n                            .targetDeviceId,\r\n                            //settings.retrievedMethod, \r\n                            settings\r\n                            .retrievedAttributeKey);\r\n                    }\r\n                } else if (settings.retrievedMethod ==\r\n                    'timeseries') {\r\n                    if (firstRetrieveValue) {\r\n                        subscribeTimeseries(\r\n                            \"entity\", // types.datasourceType.entity, in types.constant.js\r\n                            \"DEVICE\", //types.entityType.device, in types.constant.js\r\n                            self.ctx.defaultSubscription\r\n                            .targetDeviceId,\r\n                            //settings.retrievedMethod, \r\n                            settings\r\n                            .retrievedTimeseriesKey);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        firstRetrieveValue = false;\r\n    }\r\n\r\n    retrieveValue();\r\n}\r\n\r\nself.onResize = function() {};\r\n\r\nself.onDestory = function() {\r\n    if (valueSubscription) {\r\n        self.ctx.subscriptionApi.removeSubscription(\r\n            valueSubscription.id);\r\n    }\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n    try{\r\n        self.ctx.$scope.convertedValue = self.ctx.data[0].data[0][1];\r\n        self.ctx.$scope.$digest();\r\n    } catch (e) {\r\n        console.log(e);\r\n    }\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"title\": {\n                \"title\": \"Widget title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n\n            \"retrievedMethod\": {\n                \"title\": \"Retrieve value using method\",\n                \"type\": \"string\",\n                \"default\": \"rpc\"\n            },\n            \"retrievedAttributeKey\": {\n                \"title\": \"Retrieved Attribute key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrievedTimeseriesKey\": {\n                \"title\": \"Retrieved Timeseries key\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"retrievedRPCMethodName\": {\n                \"title\": \"Retrieved value using server-side RPC method name\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"requestTimeout\": {\n                \"title\": \"RPC request timeout\",\n                \"type\": \"number\",\n                \"default\": 5000\n            },\n            \"convertRetrievedValueFunction\": {\n                \"title\": \"Function body to parse retrieved value, f(retrievedValue)\",\n                \"type\": \"string\",\n                \"default\": \"return retrievedValue; //return true;\"\n            },\n\n            \"settingList\": {\n                \"type\": \"array\",\n                \"title\": \"Setting items\",\n                \"minItems\": 1,\n                \"maxItems\": 30,\n                \"items\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"icon\": {\n                            \"title\": \"Material icon, e.g., ac_unit, timer, settings and build.\",\n                            \"type\": \"string\",\n                            \"default\": \"settings\"\n                        },\n                        \"text\": {\n                            \"title\": \"Text\",\n                            \"type\": \"string\",\n                            \"default\": \"\"\n                        },\n                        \"isDisabledFunc\": {\n                            \"title\": \"Method body to detect if it is disabled, f(convertedValue), returns true or false\",\n                            \"type\": \"string\",\n                            \"default\": \"return false; //console.log(convertedValue);\"\n                        },\n                        \"customActionFunc\": {\n                            \"title\": \"Method body to action, f(convertedValue, widgetContext, openRightLayout)\",\n                            \"type\": \"string\",\n                            \"default\": \"//self.ctx.stateController.openState(state_id, widgetContext, openRightLayout); //self.ctx.stateController.updateState(...);\"\n                        }\n                    },\n                    \"required\": [\n                        \"text\",\n                        \"isDisabledFunc\",\n                        \"customActionFunc\"\n                    ]\n                }\n            }\n        },\n\n        \"required\": [\n            \"retrievedMethod\",\n            \"convertRetrievedValueFunction\",\n            \"settingList\"\n        ]\n    },\n    \"form\": [\n        \"title\",\n\n        {\n            \"key\": \"retrievedMethod\",\n            \"type\": \"rc-select\",\n            \"multiple\": false,\n            \"items\": [{\n                    \"value\": \"none\",\n                    \"label\": \"Don't retrieve\"\n                },\n                {\n                    \"value\": \"attribute\",\n                    \"label\": \"Subscribe for attribute\"\n                },\n                {\n                    \"value\": \"timeseries\",\n                    \"label\": \"Subscribe for timeseries\"\n                },\n                {\n                    \"value\": \"rpc\",\n                    \"label\": \"Call server-dide RPC to get value\"\n                }\n            ]\n        },\n        {\n            \"key\": \"retrievedAttributeKey\",\n            \"condition\": \"model.retrievedMethod === 'attribute' || model.retrievedMethod === 'SHARED_SCOPE' || model.retrievedMethod === 'SERVER_SCOPE'\"\n        },\n        {\n            \"key\": \"retrievedTimeseriesKey\",\n            \"condition\": \"model.retrievedMethod === 'timeseries'\"\n        },\n        {\n            \"key\": \"retrievedRPCMethodName\",\n            \"condition\": \"model.retrievedMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"requestTimeout\",\n            \"condition\": \"model.retrievedMethod === 'rpc'\"\n        },\n        {\n            \"key\": \"convertRetrievedValueFunction\",\n            \"type\": \"javascript\"\n        },\n        {\n            \"type\": \"help\",\n            \"description\": \"<I><small>Note: The above are the params related to retrieve value from device.</small></I>\"\n        },\n\n        {\n            \"key\": \"settingList\",\n            \"add\": \"New\",\n            \"style\": {\n                \"add\": \"btn-success\"\n            },\n            \"items\": [\n                \"settingList[].icon\",\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: refer to https://mui.com/material-ui/material-icons for more information about material-icons.</small></I>\"\n                },\n                \"settingList[].text\",\n                {\n                    \"key\": \"settingList[].isDisabledFunc\",\n                    \"type\": \"javascript\"\n                },\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: return true to display this item, return false to not display.</small></I>\"\n                },\n                {\n                    \"key\": \"settingList[].customActionFunc\",\n                    \"type\": \"javascript\"\n                },\n                {\n                    \"type\": \"help\",\n                    \"description\": \"<I><small>Note: Update state, open state, or open new dashboard.</small></I>\"\n                }\n            ]\n        }\n    ]\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px 10px\",\"settings\":{\"requestTimeout\":500,\"convertRetrievedValueFunction\":\"return retrievedValue;\",\"settingList\":[{\"icon\":\"date_range\",\"isShowFunc\":\"return true; //console.log(convertedValue);\",\"text\":\"Date\",\"isDisabledFunc\":\"return false; //console.log(convertedValue);\",\"customActionFunc\":\"//self.ctx.stateController.openState(state_id, stateParams, openRightLayout); //self.ctx.stateController.updateState(...);\"},{\"icon\":\"timer\",\"isShowFunc\":\"return true; //console.log(convertedValue);\",\"customActionFunc\":\"//self.ctx.stateController.openState(state_id, stateParams, openRightLayout); //self.ctx.stateController.updateState(...); \",\"text\":\"Time\",\"isDisabledFunc\":\"return false; //console.log(convertedValue);\"}],\"retrievedMethod\":\"rpc\",\"retrievedRPCMethodName\":\"getValue\"},\"title\":\"Setting list\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    },
    {
      "fqn": "avantec_widgets.buttons_navigation_bar",
      "name": "Buttons navigation bar",
      "deprecated": false,
      "image": null,
      "description": null,
      "descriptor": {
        "type": "rpc",
        "sizeX": 9.5,
        "sizeY": 2,
        "resources": [],
        "templateHtml": "<div class=\"my-tbs-navigation\" fxFlex fxLayout=\"column\"\r\n    style=\"height: 100%;\"\r\n    fxLayoutAlign=\"space-around stretch\">\r\n\r\n    <div fxFlex=\"{{showTitle ? 20 : 0}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '5px': '0px'}\"\r\n        class=\"title-container\" fxLayout=\"row\"\r\n        fxLayoutAlign=\"start center\" [fxShow]=\"showTitle\">\r\n        <span class=\"button-title\">{{title}}</span>\r\n    </div>\r\n\r\n    <div fxFlex=\"{{showTitle ? 80 : 100}}\"\r\n        [ngStyle]=\"{paddingTop: showTitle ? '0px': '0px'}\"\r\n        class=\"button-container\" fxLayout=\"row\" style=\"column-gap: 5px;\"\r\n        fxLayoutAlign=\"center center\">\r\n\r\n            <div class='card' *ngFor=\"let btn of buttons; index as index\" (click)=\"onSelectedBtnChange(index)\">\r\n                <div class='content' id=\"{{btn.name}}\">\r\n                    <div class='description' id=\"{{btn.name}}\"\r\n                        fxLayout=\"column\" fxLayoutAlign=\"center center\"\r\n                        [ngStyle]=\"{opacity: index==buttonsSelected ? '0.4' : '1.0'}\">\r\n                        \r\n                        <u>{{btn.name}}</u>\r\n                        <mat-icon class=\"example-tab-icon\">\r\n                            {{btn.icon}}\r\n                        </mat-icon>\r\n                        \r\n                    </div>\r\n                </div>\r\n            </div>\r\n\r\n            <!--<button mat-button extended *ngFor=\"let btn of buttons\" (click)=\"updateValue(x)\">-->\r\n            <!--    <mat-icon class=\"example-tab-icon\">-->\r\n            <!--        {{btn.icon}}-->\r\n            <!--    </mat-icon>-->\r\n            <!--    {{btn.name}}-->\r\n            <!--</button>-->\r\n            \r\n                <!--[class.mat-raised-button]=\"optionStyle?.isRaised\"-->\r\n                <!--[class.mat-mdc-raised-button]=\"optionStyle?.isRaised\"-->\r\n                <!--[color]=\"(x.attributeValue===currentValue ? optionStyle?.checkedOption.isPrimary : optionStyle?.uncheckedOption.isPrimary) ? 'primary' : ''\" -->\r\n                <!--[ngStyle]=\"x.attributeValue===currentValue ? customCheckedStyle : customUncheckedStyle\"-->\r\n\r\n        <!--<mat-tab-group mat-stretch-tabs -->\r\n        <!--     [headerPosition]=\"position\" [attr.mat-align-tabs]=\"align\"-->\r\n        <!--     [color]=\"color\" [backgroundColor]=\"bgColor\"-->\r\n        <!--    animationDuration=\"0ms\"-->\r\n        <!--    (selectedTabChange)=\"onSelectedBtnChange($event)\"-->\r\n        <!--    [selectedIndex]=\"tabsSelected\">-->\r\n        <!--    <mat-tab *ngFor=\"let tab of tabs\">-->\r\n        <!--        <ng-template mat-tab-label>-->\r\n        <!--            <div *ngIf=\"tab.icon\">-->\r\n        <!--                <mat-icon class=\"example-tab-icon\">-->\r\n        <!--                    {{tab.icon}}</mat-icon>-->\r\n        <!--            </div>{{tab.name}}-->\r\n        <!--        </ng-template>-->\r\n        <!--    </mat-tab>-->\r\n        <!--</mat-tab-group>-->\r\n    </div>\r\n\r\n    <div class=\"error-container\"\r\n        [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\r\n        fxLayout=\"row\" fxLayoutAlign=\"center center\">\r\n        <span class=\"button-error\">{{ error }}</span>\r\n    </div>\r\n</div>",
        "templateCss": ".my-tbs-navigation {\r\n    width: 100%;\r\n    height: 100%;\r\n}\r\n\r\n.my-tbs-navigation .title-container {\r\n    font-size: 0.8em;\r\n    font-weight: normal;\r\n    white-space: nowrap;\r\n    opacity: 0.5;\r\n    /*margin: 10px;*/\r\n    margin: 0px;\r\n}\r\n\r\n/*.my-tbs-navigation .button-container{*/\r\n    /*min-width: 80%;*/\r\n/*    min-width: 40%;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .mat-tab-group {*/\r\n/*    width: 100%;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .mat-tab-label {*/\r\n/*    padding: 0 2px;*/\r\n    /*min-width: 80px*/\r\n/*}*/\r\n\r\n.my-tbs-navigation .button-container .example-tab-icon{\r\n    /*margin-right: 0px;*/\r\n    vertical-align: sub;\r\n\r\n    /*font-size: 14px;*/\r\n    /*height: 14px;*/\r\n    /*width: 14px;*/\r\n    \r\n    font-size: 18px;\r\n    height: 18px;\r\n    width: 18px;\r\n}\r\n\r\n/*.my-tbs-navigation .button-container .mat-button{*/\r\n    /*width: 100%;*/\r\n/*    margin: 0;*/\r\n/*    padding: 0 0px;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .material-icons, .material-icons-round {*/\r\n    /*font-size: 24px;*/\r\n/*    font-size: 16px;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .mat-icon {*/\r\n    /*height: 24px;*/\r\n    /*width: 24px;*/\r\n/*    height: 16px;*/\r\n/*    width: 16px;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .mat-mdc-tab-header .mdc-tab {*/\r\n    /*height: 48px;*/\r\n/*    height: 20px;*/\r\n/*}*/\r\n\r\n/*.my-tbs-navigation .button-container .mdc-tab {*/\r\n    /*min-width: 90px;*/\r\n    /*padding-right: 24px;*/\r\n    /*padding-left: 24px;*/\r\n/*    min-width: 50px;*/\r\n/*    padding-right: 4px;*/\r\n/*    padding-left: 4px;*/\r\n/*}*/\r\n\r\n.my-tbs-navigation .button-container .card {\r\n   width: 100%;\r\n   height: 100%;\r\n   box-sizing: border-box;\r\n   background-color: #fff;\r\n}\r\n\r\n.my-tbs-navigation .button-container .card .content {\r\n   padding: 5px;\r\n   display: flex;\r\n   flex-direction: column;\r\n   align-items: center;\r\n   justify-content: center;\r\n   height: 100%;\r\n   box-sizing: border-box;\r\n   transition: background-color 0.5s;\r\n}\r\n\r\n.my-tbs-navigation .button-container .card .content:hover {\r\n    background-color: #ddd;\r\n}\r\n\r\n.my-tbs-navigation .button-container .card .description {\r\n    font-size: 0.75em;\r\n    font-weight: normal;\r\n    line-height: 2;\r\n    color: #333333;\r\n    text-transform: uppercase;\r\n}\r\n\r\n.my-tbs-navigation .error-container {\r\n    position: absolute;\r\n    top: 2%;\r\n    right: 0;\r\n    left: 0;\r\n    z-index: 4;\r\n    height: 14px;\r\n}\r\n\r\n.my-tbs-navigation .error-container .button-error {\r\n    color: #ff3315;\r\n    white-space: nowrap;\r\n}\r\n\r\n\r\n",
        "controllerScript": "// v.2.4, 2023-09-25 17:00\r\n\r\nself.onInit = function() {\r\n    self.ctx.$scope.onSelectedBtnChange = function(index) {\r\n        //console.log(\"self.ctx\", self.ctx);\r\n        console.log(\"index=\", index);\r\n        \r\n        //console.log(\"self.ctx.$scope.buttonsSelected=\", self.ctx.$scope.buttonsSelected);\r\n\r\n        if (self.ctx.$scope.buttonsSelected == index) {\r\n            return;\r\n        } else {\r\n            self.ctx.$scope.buttonsSelected = index;\r\n        }\r\n        \r\n        let entityInfo = self.ctx.actionsApi.getActiveEntityInfo();\r\n        //entityInfo.entityId, entityInfo.entityLabel, entityInfo.entityName\r\n        //console.log(entityInfo);\r\n        \r\n        const id_ = entityInfo ? entityInfo.id : null\r\n            , entityType_ = entityInfo ? entityInfo.entityType : null\r\n            , entityId = {entityType: entityType_, id: id_}\r\n            , entityName = entityInfo ? entityInfo.entityName : null\r\n            , entityLabel = entityInfo ? entityInfo.entityLabel : null;\r\n\r\n        if (!self.ctx.isEdit && (index<self.ctx.$scope.buttons.length)) //entityInfo &&\r\n        {\r\n            const descriptor = self.ctx.$scope.buttons[index];\r\n            //console.log(\"descriptor=\", descriptor);\r\n            if (descriptor) {\r\n                //event.stopPropagation();\r\n                // self.ctx.actionsApi.handleWidgetAction($event, descriptor, entityId, entityName);\r\n                self.ctx.actionsApi.handleWidgetAction(null, descriptor,\r\n                    entityId, entityName, null, entityLabel);\r\n            }\r\n        }\r\n    };\r\n\r\n    ////self.ctx.ngZone.run(function() {\r\n       init();\r\n    //  self.ctx.detectChanges(true);\r\n    //});\r\n};\r\n\r\nfunction init() {\r\n    let utils       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n    //let types       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('types'));\r\n    //let toast       = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('toast'));\r\n    //let translate   = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n    //let $q          = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('$q'));\r\n    //let http        = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('http'));\r\n    //let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n    //let rpcUpdateStatus = {};\r\n    let settings = utils.deepClone(self.ctx.settings) || {}; \r\n    \r\n    self.ctx.$scope.toastTargetId = 'input-widget' + utils.guid();\r\n    self.ctx.$scope.showTitle = settings.title && settings.title.length ? true : false; ////utils.defaultValue(settings.title, true);\r\n    self.ctx.$scope.title = settings.title;\r\n    // self.ctx.$scope.position = settings.position;\r\n    // self.ctx.$scope.align = settings.align;\r\n    //self.ctx.$scope.minWidth = settings.minWidth;\r\n    //self.ctx.$scope.isPrimary = settings.isPrimary; //\r\n    self.ctx.$scope.color = settings.color; //\r\n    self.ctx.$scope.bgColor = settings.bgColor;\r\n    self.ctx.$scope.buttons = self.ctx.actionsApi.getActionDescriptors(\"elementClick\");  ////actionDescriptors = ////settings.buttons;\r\n    self.ctx.$scope.buttonsSelected = settings.buttonsSelected;\r\n    if (self.ctx.$scope.buttonsSelected<0) {\r\n        self.ctx.$scope.buttonsSelected = 0;\r\n    } else if (self.ctx.$scope.buttonsSelected>=self.ctx.$scope.buttons.length) {\r\n        self.ctx.$scope.buttonsSelected = self.ctx.$scope.buttons.length-1;\r\n    }\r\n    \r\n    // console.log(\"actionDescriptors=\", self.ctx.$scope.buttons);\r\n\r\n    /*self.ctx.$scope.tabGroupStyle = {};\r\n    self.ctx.$scope.customCheckedStyle['min-width'] = '40px';\r\n    self.ctx.$scope.customCheckedStyle['padding']   = '0 2px';\r\n    if (self.ctx.settings.optionStyle.orientation === 'column') {\r\n        self.ctx.$scope.customCheckedStyle['width']     = '100%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    } else {\r\n        self.ctx.$scope.customCheckedStyle['width']     = 100/self.ctx.settings.options.length + '%';\r\n        self.ctx.$scope.customCheckedStyle['margin']    = 0;\r\n    }\r\n    if (self.ctx.settings.isPrimary === false) {\r\n        self.ctx.$scope.tabGroupStyle['background-color']  = self.ctx.$scope.bgColor;\r\n        self.ctx.$scope.tabGroupStyle['color']             = self.ctx.$scope.color;\r\n    }*/\r\n\r\n    //console.log(\"onInit\", self.ctx.$scope.align, self.ctx.$scope.position);\r\n}\r\n\r\nself.onResize = function() {\r\n    //    min-width: 80%\r\n};\r\n\r\nself.actionSources = function() {\r\n    return {\r\n        'elementClick': {\r\n            name: 'widget-action.element-click',\r\n            multiple: true\r\n        }\r\n    };\r\n};\r\n\r\nself.onDestory = function() {\r\n};\r\n\r\n/*self.onDataUpdated = function() {\r\n}*/\r\n\r\n/*self.typeParameters = function() {\r\n    return {\r\n        maxDatasources: 1, // Maximum allowed datasources for this widget, -1 - unlimited\r\n        maxDataKeys: 1 //Maximum allowed data keys for this widget, -1 - unlimited\r\n    }\r\n};*/\r\n",
        "settingsSchema": "{\r\n    \"schema\": {\r\n        \"type\": \"object\",\r\n        \"title\": \"Settings\",\r\n        \"properties\": {\r\n            \"title\": {\r\n                \"title\": \"Widget title\",\r\n                \"type\": \"string\",\r\n                \"default\": \"\"\r\n            },\r\n            \r\n            \"buttonsSelected\": {\r\n                \"title\": \"Buttons selected\",\r\n                \"type\": \"integer\",\r\n                \"default\": \"0\"\r\n            },\r\n            \"color\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Buttons color\",\r\n                \"default\": \"\"\r\n            },\r\n            \"bgColor\": {\r\n                \"type\": \"string\",\r\n                \"title\": \"Buttons background color\",\r\n                \"default\": \"\"\r\n            }\r\n        },\r\n            \r\n        \"required\": [\r\n            \"buttonsSelected\"\r\n        ]\r\n    },\r\n\r\n    \"form\": [\r\n        \"title\",\r\n\r\n        \"buttonsSelected\",\r\n        {\r\n            \"key\": \"bgColor\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        },\r\n        {\r\n            \"key\": \"color\",\r\n            \"type\": \"rc-select\",\r\n            \"multiple\": false,\r\n            \"items\": [\r\n                {\r\n                    \"value\": \"\",\r\n                    \"label\": \"null\"\r\n                },\r\n                {\r\n                    \"value\": \"primary\",\r\n                    \"label\": \"primary\"\r\n                },\r\n                {\r\n                    \"value\": \"accent\",\r\n                    \"label\": \"accent\"\r\n                },\r\n                {\r\n                    \"value\": \"warn\",\r\n                    \"label\": \"warn\"\r\n                }\r\n            ]\r\n        },\r\n        {\r\n            \"type\": \"help\",\r\n            \"description\": \"Refer to https://material.angular.io/guide/theming#themes for more information about primary, accent and warn.\"\r\n        }\r\n    ]\r\n}",
        "dataKeySettingsSchema": "{}\n",
        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#EEEEEE\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"buttonsSelected\":0},\"title\":\"Buttons navigation bar\",\"actions\":{\"elementClick\":[{\"name\":\"AAAA\",\"icon\":\"now_widgets\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Button label</b>: ' + 'AAAA';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"e6bd9a5b-b2fa-233a-f7f7-271e9c742194\"},{\"name\":\"BBBB\",\"icon\":\"extension\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Button label</b>: ' + 'BBBB';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"2fc2648c-0071-5a04-437b-2a7ed2240a69\"},{\"name\":\"CCCC\",\"icon\":\"bedroom_baby\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"custom\",\"customFunction\":\"var title;\\r\\nvar content;\\r\\n// if (entityName) {\\r\\n//   if (additionalParams && additionalParams.entity) {\\r\\n//     var entity = additionalParams.entity;\\r\\n//     if (entity.id) {\\r\\n//       content += '<br><b>Entity type</b>: ' + entity.id.entityType;\\r\\n//     }\\r\\n//     if (!isNaN(entity.temperature) && entity.temperature !== '') {\\r\\n//       content += '<br><b>Temperature</b>: ' + entity.temperature + ' °C';\\r\\n//     }\\r\\n//   }\\r\\n// } else {\\r\\n//   title = 'No entity information available';\\r\\n//   content = '<b>No entity information available</b>';\\r\\n// }\\r\\n\\r\\ntitle = 'Details';\\r\\ncontent = '<b>Button label</b>: ' + 'CCCC';\\r\\n\\r\\nshowAlertDialog(title, content);\\r\\n\\r\\nfunction showAlertDialog(title, content) {\\r\\n  setTimeout(function() {\\r\\n    widgetContext.dialogs.alert(title, content).subscribe();\\r\\n  }, 100);\\r\\n}\\r\\n\",\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"c7644acb-0f1e-18ab-edf0-71b0204c62e9\"}]},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":false,\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
      },
      "externalId": null
    }
  ]
}