diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 67a076bd46..6f4685c018 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -64,7 +64,7 @@ "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\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;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" } @@ -80,9 +80,9 @@ "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\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;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Round switch\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" } }, { @@ -96,9 +96,9 @@ "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"scheckStatusMethod\": {\n \"title\": \"Check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"scheckStatusMethod\", \"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"scheckStatusMethod\",\n \"valueAttribute\",\n \"requestTimeout\"\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"scheckStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"scheckStatusMethod\", \"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"scheckStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"scheckStatusMethod\":\"checkStatus\",\"valueAttribute\":\"value\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"scheckStatusMethod\":\"checkStatus\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" } } ] diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 61b3e8a8e3..6ebe3c98ef 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -70,7 +70,7 @@ public class ThingsboardInstallService { switch (upgradeFromVersion) { case "1.2.3": - log.info("Upgrading ThingsBoard from version {} to 1.3.0 ...", upgradeFromVersion); + log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ..."); databaseUpgradeService.upgradeDatabase(upgradeFromVersion); @@ -84,6 +84,21 @@ public class ThingsboardInstallService { systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets"); systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets"); + case "1.3.0": + log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ..."); + + log.info("Updating system data..."); + + systemDataLoaderService.deleteSystemWidgetBundle("charts"); + systemDataLoaderService.deleteSystemWidgetBundle("cards"); + systemDataLoaderService.deleteSystemWidgetBundle("maps"); + systemDataLoaderService.deleteSystemWidgetBundle("analogue_gauges"); + systemDataLoaderService.deleteSystemWidgetBundle("digital_gauges"); + systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets"); + systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets"); + systemDataLoaderService.deleteSystemWidgetBundle("control_widgets"); + systemDataLoaderService.deleteSystemWidgetBundle("maps_v2"); + systemDataLoaderService.loadSystemWidgets(); break; diff --git a/ui/src/app/widget/lib/rpc/led-indicator.directive.js b/ui/src/app/widget/lib/rpc/led-indicator.directive.js index 0487ee3501..04615c7b98 100644 --- a/ui/src/app/widget/lib/rpc/led-indicator.directive.js +++ b/ui/src/app/widget/lib/rpc/led-indicator.directive.js @@ -116,6 +116,20 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { if (vm.ctx.settings.requestTimeout) { vm.requestTimeout = vm.ctx.settings.requestTimeout; } + vm.retrieveValueMethod = 'attribute'; + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) { + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod; + } + + vm.parseValueFunction = (data) => data ? true : false; + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) { + try { + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction); + } catch (e) { + vm.parseValueFunction = (data) => data ? true : false; + } + } + vm.checkStatusMethod = 'checkStatus'; if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) { vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod; @@ -222,11 +236,19 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { var subscriptionsInfo = [{ type: types.datasourceType.entity, entityType: types.entityType.device, - entityId: vm.ctx.defaultSubscription.targetDeviceId, - attributes: [ - {name: vm.valueAttribute} - ] + entityId: vm.ctx.defaultSubscription.targetDeviceId }]; + + if (vm.retrieveValueMethod == 'attribute') { + subscriptionsInfo[0].attributes = [ + {name: vm.valueAttribute} + ]; + } else { + subscriptionsInfo[0].timeseries = [ + {name: vm.valueAttribute} + ]; + } + vm.ctx.subscriptionApi.createSubscriptionFromInfo ( types.widgetType.latest.value, subscriptionsInfo, vm.subscriptionOptions, false, true).then( function(subscription) { @@ -245,7 +267,7 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { if (attrValue) { var parsed = null; try { - parsed = angular.fromJson(attrValue); + parsed = vm.parseValueFunction(angular.fromJson(attrValue)); } catch (e){/**/} value = parsed ? true : false; } diff --git a/ui/src/app/widget/lib/rpc/round-switch.directive.js b/ui/src/app/widget/lib/rpc/round-switch.directive.js index edb8c7bb68..4f85ff28ff 100644 --- a/ui/src/app/widget/lib/rpc/round-switch.directive.js +++ b/ui/src/app/widget/lib/rpc/round-switch.directive.js @@ -41,7 +41,7 @@ function RoundSwitch() { } /*@ngInject*/ -function RoundSwitchController($element, $scope, utils) { +function RoundSwitchController($element, $scope, utils, types) { let vm = this; vm.showTitle = false; @@ -64,12 +64,20 @@ function RoundSwitchController($element, $scope, utils) { onValue(); }); + vm.valueSubscription = null; + $scope.$watch('vm.ctx', () => { if (vm.ctx) { init(); } }); + $scope.$on('$destroy', () => { + if (vm.valueSubscription) { + vm.ctx.subscriptionApi.removeSubscription(vm.valueSubscription.id); + } + }); + resize(); function init() { @@ -92,6 +100,35 @@ function RoundSwitchController($element, $scope, utils) { if (vm.ctx.settings.requestTimeout) { vm.requestTimeout = vm.ctx.settings.requestTimeout; } + + vm.retrieveValueMethod = 'rpc'; + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) { + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod; + } + + vm.valueKey = 'value'; + if (vm.ctx.settings.valueKey && vm.ctx.settings.valueKey.length) { + vm.valueKey = vm.ctx.settings.valueKey; + } + + vm.parseValueFunction = (data) => data ? true : false; + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) { + try { + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction); + } catch (e) { + vm.parseValueFunction = (data) => data ? true : false; + } + } + + vm.convertValueFunction = (value) => value; + if (vm.ctx.settings.convertValueFunction && vm.ctx.settings.convertValueFunction.length) { + try { + vm.convertValueFunction = new Function('value', vm.ctx.settings.convertValueFunction); + } catch (e) { + vm.convertValueFunction = (value) => value; + } + } + vm.getValueMethod = 'getValue'; if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { vm.getValueMethod = vm.ctx.settings.getValueMethod; @@ -104,7 +141,11 @@ function RoundSwitchController($element, $scope, utils) { onError('Target device is not set!'); } else { if (!vm.isSimulated) { - rpcRequestValue(); + if (vm.retrieveValueMethod == 'rpc') { + rpcRequestValue(); + } else if (vm.retrieveValueMethod == 'attribute' || vm.retrieveValueMethod == 'timeseries') { + subscribeForValue(); + } } } } @@ -127,6 +168,66 @@ function RoundSwitchController($element, $scope, utils) { setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); } + function subscribeForValue() { + var valueSubscriptionInfo = [{ + type: types.datasourceType.entity, + entityType: types.entityType.device, + entityId: vm.ctx.defaultSubscription.targetDeviceId + }]; + if (vm.retrieveValueMethod == 'attribute') { + valueSubscriptionInfo[0].attributes = [ + {name: vm.valueKey} + ]; + } else { + valueSubscriptionInfo[0].timeseries = [ + {name: vm.valueKey} + ]; + } + var subscriptionOptions = { + callbacks: { + onDataUpdated: onDataUpdated, + onDataUpdateError: onDataUpdateError + } + }; + vm.ctx.subscriptionApi.createSubscriptionFromInfo ( + types.widgetType.latest.value, valueSubscriptionInfo, subscriptionOptions, false, true).then( + (subscription) => { + vm.valueSubscription = subscription; + } + ); + } + + function onDataUpdated(subscription, apply) { + var value = false; + var data = subscription.data; + if (data.length) { + var keyData = data[0]; + if (keyData && keyData.data && keyData.data[0]) { + var attrValue = keyData.data[0][1]; + if (attrValue) { + var parsed = null; + try { + parsed = vm.parseValueFunction(angular.fromJson(attrValue)); + } catch (e){/**/} + value = parsed ? true : false; + } + } + } + setValue(value); + if (apply) { + $scope.$digest(); + } + } + + function onDataUpdateError(subscription, e) { + var exceptionData = utils.parseException(e); + var errorText = exceptionData.name; + if (exceptionData.message) { + errorText += ': ' + exceptionData.message; + } + onError(errorText); + } + function setValue(value) { vm.value = value ? true : false; onoff.prop('checked', !vm.value); @@ -162,7 +263,7 @@ function RoundSwitchController($element, $scope, utils) { vm.error = ''; vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( (responseBody) => { - setValue(responseBody); + setValue(vm.parseValueFunction(responseBody)); }, () => { var errorText = vm.ctx.defaultSubscription.rpcErrorText; @@ -181,7 +282,7 @@ function RoundSwitchController($element, $scope, utils) { vm.executingUpdateValue = true; } vm.error = ''; - vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, vm.convertValueFunction(value), vm.requestTimeout).then( () => { vm.executingUpdateValue = false; if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) { diff --git a/ui/src/app/widget/lib/rpc/switch.directive.js b/ui/src/app/widget/lib/rpc/switch.directive.js index b84b2f1c36..6256165872 100644 --- a/ui/src/app/widget/lib/rpc/switch.directive.js +++ b/ui/src/app/widget/lib/rpc/switch.directive.js @@ -41,7 +41,7 @@ function Switch() { } /*@ngInject*/ -function SwitchController($element, $scope) { +function SwitchController($element, $scope, types, utils) { let vm = this; vm.showTitle = false; @@ -63,12 +63,20 @@ function SwitchController($element, $scope) { vm.onValue = onValue; + vm.valueSubscription = null; + $scope.$watch('vm.ctx', () => { if (vm.ctx) { init(); } }); + $scope.$on('$destroy', () => { + if (vm.valueSubscription) { + vm.ctx.subscriptionApi.removeSubscription(vm.valueSubscription.id); + } + }); + function init() { vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : ''; @@ -90,6 +98,35 @@ function SwitchController($element, $scope) { if (vm.ctx.settings.requestTimeout) { vm.requestTimeout = vm.ctx.settings.requestTimeout; } + + vm.retrieveValueMethod = 'rpc'; + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) { + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod; + } + + vm.valueKey = 'value'; + if (vm.ctx.settings.valueKey && vm.ctx.settings.valueKey.length) { + vm.valueKey = vm.ctx.settings.valueKey; + } + + vm.parseValueFunction = (data) => data ? true : false; + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) { + try { + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction); + } catch (e) { + vm.parseValueFunction = (data) => data ? true : false; + } + } + + vm.convertValueFunction = (value) => value; + if (vm.ctx.settings.convertValueFunction && vm.ctx.settings.convertValueFunction.length) { + try { + vm.convertValueFunction = new Function('value', vm.ctx.settings.convertValueFunction); + } catch (e) { + vm.convertValueFunction = (value) => value; + } + } + vm.getValueMethod = 'getValue'; if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { vm.getValueMethod = vm.ctx.settings.getValueMethod; @@ -102,7 +139,11 @@ function SwitchController($element, $scope) { onError('Target device is not set!'); } else { if (!vm.isSimulated) { - rpcRequestValue(); + if (vm.retrieveValueMethod == 'rpc') { + rpcRequestValue(); + } else if (vm.retrieveValueMethod == 'attribute' || vm.retrieveValueMethod == 'timeseries') { + subscribeForValue(); + } } } } @@ -144,6 +185,66 @@ function SwitchController($element, $scope) { setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); } + function subscribeForValue() { + var valueSubscriptionInfo = [{ + type: types.datasourceType.entity, + entityType: types.entityType.device, + entityId: vm.ctx.defaultSubscription.targetDeviceId + }]; + if (vm.retrieveValueMethod == 'attribute') { + valueSubscriptionInfo[0].attributes = [ + {name: vm.valueKey} + ]; + } else { + valueSubscriptionInfo[0].timeseries = [ + {name: vm.valueKey} + ]; + } + var subscriptionOptions = { + callbacks: { + onDataUpdated: onDataUpdated, + onDataUpdateError: onDataUpdateError + } + }; + vm.ctx.subscriptionApi.createSubscriptionFromInfo ( + types.widgetType.latest.value, valueSubscriptionInfo, subscriptionOptions, false, true).then( + (subscription) => { + vm.valueSubscription = subscription; + } + ); + } + + function onDataUpdated(subscription, apply) { + var value = false; + var data = subscription.data; + if (data.length) { + var keyData = data[0]; + if (keyData && keyData.data && keyData.data[0]) { + var attrValue = keyData.data[0][1]; + if (attrValue) { + var parsed = null; + try { + parsed = vm.parseValueFunction(angular.fromJson(attrValue)); + } catch (e){/**/} + value = parsed ? true : false; + } + } + } + setValue(value); + if (apply) { + $scope.$digest(); + } + } + + function onDataUpdateError(subscription, e) { + var exceptionData = utils.parseException(e); + var errorText = exceptionData.name; + if (exceptionData.message) { + errorText += ': ' + exceptionData.message; + } + onError(errorText); + } + function setValue(value) { vm.value = value ? true : false; } @@ -178,7 +279,7 @@ function SwitchController($element, $scope) { vm.error = ''; vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( (responseBody) => { - setValue(responseBody); + setValue(vm.parseValueFunction(responseBody)); }, () => { var errorText = vm.ctx.defaultSubscription.rpcErrorText; @@ -197,7 +298,7 @@ function SwitchController($element, $scope) { vm.executingUpdateValue = true; } vm.error = ''; - vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, vm.convertValueFunction(value), vm.requestTimeout).then( () => { vm.executingUpdateValue = false; if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {