Commit c1e77ccad33e39408e1b228dc6efd76c3a473ff6

Authored by Igor Kulikov
1 parent 28853c7d

UI: Improved configuration of RPC control widgets

@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 "templateHtml": "<tb-switch ctx='ctx'></tb-switch>", 64 "templateHtml": "<tb-switch ctx='ctx'></tb-switch>",
65 "templateCss": "", 65 "templateCss": "",
66 "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", 66 "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",
67 - "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}", 67 + "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}",
68 "dataKeySettingsSchema": "{}\n", 68 "dataKeySettingsSchema": "{}\n",
69 "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}" 69 "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}"
70 } 70 }
@@ -80,9 +80,9 @@ @@ -80,9 +80,9 @@
80 "templateHtml": "<tb-round-switch ctx='ctx'></tb-round-switch>", 80 "templateHtml": "<tb-round-switch ctx='ctx'></tb-round-switch>",
81 "templateCss": "", 81 "templateCss": "",
82 "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", 82 "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",
83 - "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}", 83 + "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}",
84 "dataKeySettingsSchema": "{}\n", 84 "dataKeySettingsSchema": "{}\n",
85 - "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}" 85 + "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}"
86 } 86 }
87 }, 87 },
88 { 88 {
@@ -96,9 +96,9 @@ @@ -96,9 +96,9 @@
96 "templateHtml": "<tb-led-indicator ctx='ctx'></tb-led-indicator>", 96 "templateHtml": "<tb-led-indicator ctx='ctx'></tb-led-indicator>",
97 "templateCss": "", 97 "templateCss": "",
98 "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", 98 "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",
99 - "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}", 99 + "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}",
100 "dataKeySettingsSchema": "{}\n", 100 "dataKeySettingsSchema": "{}\n",
101 - "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}" 101 + "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}"
102 } 102 }
103 } 103 }
104 ] 104 ]
@@ -70,7 +70,7 @@ public class ThingsboardInstallService { @@ -70,7 +70,7 @@ public class ThingsboardInstallService {
70 70
71 switch (upgradeFromVersion) { 71 switch (upgradeFromVersion) {
72 case "1.2.3": 72 case "1.2.3":
73 - log.info("Upgrading ThingsBoard from version {} to 1.3.0 ...", upgradeFromVersion); 73 + log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ...");
74 74
75 databaseUpgradeService.upgradeDatabase(upgradeFromVersion); 75 databaseUpgradeService.upgradeDatabase(upgradeFromVersion);
76 76
@@ -84,6 +84,21 @@ public class ThingsboardInstallService { @@ -84,6 +84,21 @@ public class ThingsboardInstallService {
84 systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets"); 84 systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets");
85 systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets"); 85 systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets");
86 86
  87 + case "1.3.0":
  88 + log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ...");
  89 +
  90 + log.info("Updating system data...");
  91 +
  92 + systemDataLoaderService.deleteSystemWidgetBundle("charts");
  93 + systemDataLoaderService.deleteSystemWidgetBundle("cards");
  94 + systemDataLoaderService.deleteSystemWidgetBundle("maps");
  95 + systemDataLoaderService.deleteSystemWidgetBundle("analogue_gauges");
  96 + systemDataLoaderService.deleteSystemWidgetBundle("digital_gauges");
  97 + systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets");
  98 + systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets");
  99 + systemDataLoaderService.deleteSystemWidgetBundle("control_widgets");
  100 + systemDataLoaderService.deleteSystemWidgetBundle("maps_v2");
  101 +
87 systemDataLoaderService.loadSystemWidgets(); 102 systemDataLoaderService.loadSystemWidgets();
88 103
89 break; 104 break;
@@ -116,6 +116,20 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { @@ -116,6 +116,20 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) {
116 if (vm.ctx.settings.requestTimeout) { 116 if (vm.ctx.settings.requestTimeout) {
117 vm.requestTimeout = vm.ctx.settings.requestTimeout; 117 vm.requestTimeout = vm.ctx.settings.requestTimeout;
118 } 118 }
  119 + vm.retrieveValueMethod = 'attribute';
  120 + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) {
  121 + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod;
  122 + }
  123 +
  124 + vm.parseValueFunction = (data) => data ? true : false;
  125 + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) {
  126 + try {
  127 + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction);
  128 + } catch (e) {
  129 + vm.parseValueFunction = (data) => data ? true : false;
  130 + }
  131 + }
  132 +
119 vm.checkStatusMethod = 'checkStatus'; 133 vm.checkStatusMethod = 'checkStatus';
120 if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) { 134 if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) {
121 vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod; 135 vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod;
@@ -222,11 +236,19 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { @@ -222,11 +236,19 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) {
222 var subscriptionsInfo = [{ 236 var subscriptionsInfo = [{
223 type: types.datasourceType.entity, 237 type: types.datasourceType.entity,
224 entityType: types.entityType.device, 238 entityType: types.entityType.device,
225 - entityId: vm.ctx.defaultSubscription.targetDeviceId,  
226 - attributes: [  
227 - {name: vm.valueAttribute}  
228 - ] 239 + entityId: vm.ctx.defaultSubscription.targetDeviceId
229 }]; 240 }];
  241 +
  242 + if (vm.retrieveValueMethod == 'attribute') {
  243 + subscriptionsInfo[0].attributes = [
  244 + {name: vm.valueAttribute}
  245 + ];
  246 + } else {
  247 + subscriptionsInfo[0].timeseries = [
  248 + {name: vm.valueAttribute}
  249 + ];
  250 + }
  251 +
230 vm.ctx.subscriptionApi.createSubscriptionFromInfo ( 252 vm.ctx.subscriptionApi.createSubscriptionFromInfo (
231 types.widgetType.latest.value, subscriptionsInfo, vm.subscriptionOptions, false, true).then( 253 types.widgetType.latest.value, subscriptionsInfo, vm.subscriptionOptions, false, true).then(
232 function(subscription) { 254 function(subscription) {
@@ -245,7 +267,7 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { @@ -245,7 +267,7 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) {
245 if (attrValue) { 267 if (attrValue) {
246 var parsed = null; 268 var parsed = null;
247 try { 269 try {
248 - parsed = angular.fromJson(attrValue); 270 + parsed = vm.parseValueFunction(angular.fromJson(attrValue));
249 } catch (e){/**/} 271 } catch (e){/**/}
250 value = parsed ? true : false; 272 value = parsed ? true : false;
251 } 273 }
@@ -41,7 +41,7 @@ function RoundSwitch() { @@ -41,7 +41,7 @@ function RoundSwitch() {
41 } 41 }
42 42
43 /*@ngInject*/ 43 /*@ngInject*/
44 -function RoundSwitchController($element, $scope, utils) { 44 +function RoundSwitchController($element, $scope, utils, types) {
45 let vm = this; 45 let vm = this;
46 46
47 vm.showTitle = false; 47 vm.showTitle = false;
@@ -64,12 +64,20 @@ function RoundSwitchController($element, $scope, utils) { @@ -64,12 +64,20 @@ function RoundSwitchController($element, $scope, utils) {
64 onValue(); 64 onValue();
65 }); 65 });
66 66
  67 + vm.valueSubscription = null;
  68 +
67 $scope.$watch('vm.ctx', () => { 69 $scope.$watch('vm.ctx', () => {
68 if (vm.ctx) { 70 if (vm.ctx) {
69 init(); 71 init();
70 } 72 }
71 }); 73 });
72 74
  75 + $scope.$on('$destroy', () => {
  76 + if (vm.valueSubscription) {
  77 + vm.ctx.subscriptionApi.removeSubscription(vm.valueSubscription.id);
  78 + }
  79 + });
  80 +
73 resize(); 81 resize();
74 82
75 function init() { 83 function init() {
@@ -92,6 +100,35 @@ function RoundSwitchController($element, $scope, utils) { @@ -92,6 +100,35 @@ function RoundSwitchController($element, $scope, utils) {
92 if (vm.ctx.settings.requestTimeout) { 100 if (vm.ctx.settings.requestTimeout) {
93 vm.requestTimeout = vm.ctx.settings.requestTimeout; 101 vm.requestTimeout = vm.ctx.settings.requestTimeout;
94 } 102 }
  103 +
  104 + vm.retrieveValueMethod = 'rpc';
  105 + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) {
  106 + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod;
  107 + }
  108 +
  109 + vm.valueKey = 'value';
  110 + if (vm.ctx.settings.valueKey && vm.ctx.settings.valueKey.length) {
  111 + vm.valueKey = vm.ctx.settings.valueKey;
  112 + }
  113 +
  114 + vm.parseValueFunction = (data) => data ? true : false;
  115 + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) {
  116 + try {
  117 + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction);
  118 + } catch (e) {
  119 + vm.parseValueFunction = (data) => data ? true : false;
  120 + }
  121 + }
  122 +
  123 + vm.convertValueFunction = (value) => value;
  124 + if (vm.ctx.settings.convertValueFunction && vm.ctx.settings.convertValueFunction.length) {
  125 + try {
  126 + vm.convertValueFunction = new Function('value', vm.ctx.settings.convertValueFunction);
  127 + } catch (e) {
  128 + vm.convertValueFunction = (value) => value;
  129 + }
  130 + }
  131 +
95 vm.getValueMethod = 'getValue'; 132 vm.getValueMethod = 'getValue';
96 if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { 133 if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
97 vm.getValueMethod = vm.ctx.settings.getValueMethod; 134 vm.getValueMethod = vm.ctx.settings.getValueMethod;
@@ -104,7 +141,11 @@ function RoundSwitchController($element, $scope, utils) { @@ -104,7 +141,11 @@ function RoundSwitchController($element, $scope, utils) {
104 onError('Target device is not set!'); 141 onError('Target device is not set!');
105 } else { 142 } else {
106 if (!vm.isSimulated) { 143 if (!vm.isSimulated) {
107 - rpcRequestValue(); 144 + if (vm.retrieveValueMethod == 'rpc') {
  145 + rpcRequestValue();
  146 + } else if (vm.retrieveValueMethod == 'attribute' || vm.retrieveValueMethod == 'timeseries') {
  147 + subscribeForValue();
  148 + }
108 } 149 }
109 } 150 }
110 } 151 }
@@ -127,6 +168,66 @@ function RoundSwitchController($element, $scope, utils) { @@ -127,6 +168,66 @@ function RoundSwitchController($element, $scope, utils) {
127 setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); 168 setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
128 } 169 }
129 170
  171 + function subscribeForValue() {
  172 + var valueSubscriptionInfo = [{
  173 + type: types.datasourceType.entity,
  174 + entityType: types.entityType.device,
  175 + entityId: vm.ctx.defaultSubscription.targetDeviceId
  176 + }];
  177 + if (vm.retrieveValueMethod == 'attribute') {
  178 + valueSubscriptionInfo[0].attributes = [
  179 + {name: vm.valueKey}
  180 + ];
  181 + } else {
  182 + valueSubscriptionInfo[0].timeseries = [
  183 + {name: vm.valueKey}
  184 + ];
  185 + }
  186 + var subscriptionOptions = {
  187 + callbacks: {
  188 + onDataUpdated: onDataUpdated,
  189 + onDataUpdateError: onDataUpdateError
  190 + }
  191 + };
  192 + vm.ctx.subscriptionApi.createSubscriptionFromInfo (
  193 + types.widgetType.latest.value, valueSubscriptionInfo, subscriptionOptions, false, true).then(
  194 + (subscription) => {
  195 + vm.valueSubscription = subscription;
  196 + }
  197 + );
  198 + }
  199 +
  200 + function onDataUpdated(subscription, apply) {
  201 + var value = false;
  202 + var data = subscription.data;
  203 + if (data.length) {
  204 + var keyData = data[0];
  205 + if (keyData && keyData.data && keyData.data[0]) {
  206 + var attrValue = keyData.data[0][1];
  207 + if (attrValue) {
  208 + var parsed = null;
  209 + try {
  210 + parsed = vm.parseValueFunction(angular.fromJson(attrValue));
  211 + } catch (e){/**/}
  212 + value = parsed ? true : false;
  213 + }
  214 + }
  215 + }
  216 + setValue(value);
  217 + if (apply) {
  218 + $scope.$digest();
  219 + }
  220 + }
  221 +
  222 + function onDataUpdateError(subscription, e) {
  223 + var exceptionData = utils.parseException(e);
  224 + var errorText = exceptionData.name;
  225 + if (exceptionData.message) {
  226 + errorText += ': ' + exceptionData.message;
  227 + }
  228 + onError(errorText);
  229 + }
  230 +
130 function setValue(value) { 231 function setValue(value) {
131 vm.value = value ? true : false; 232 vm.value = value ? true : false;
132 onoff.prop('checked', !vm.value); 233 onoff.prop('checked', !vm.value);
@@ -162,7 +263,7 @@ function RoundSwitchController($element, $scope, utils) { @@ -162,7 +263,7 @@ function RoundSwitchController($element, $scope, utils) {
162 vm.error = ''; 263 vm.error = '';
163 vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( 264 vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
164 (responseBody) => { 265 (responseBody) => {
165 - setValue(responseBody); 266 + setValue(vm.parseValueFunction(responseBody));
166 }, 267 },
167 () => { 268 () => {
168 var errorText = vm.ctx.defaultSubscription.rpcErrorText; 269 var errorText = vm.ctx.defaultSubscription.rpcErrorText;
@@ -181,7 +282,7 @@ function RoundSwitchController($element, $scope, utils) { @@ -181,7 +282,7 @@ function RoundSwitchController($element, $scope, utils) {
181 vm.executingUpdateValue = true; 282 vm.executingUpdateValue = true;
182 } 283 }
183 vm.error = ''; 284 vm.error = '';
184 - vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( 285 + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, vm.convertValueFunction(value), vm.requestTimeout).then(
185 () => { 286 () => {
186 vm.executingUpdateValue = false; 287 vm.executingUpdateValue = false;
187 if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) { 288 if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {
@@ -41,7 +41,7 @@ function Switch() { @@ -41,7 +41,7 @@ function Switch() {
41 } 41 }
42 42
43 /*@ngInject*/ 43 /*@ngInject*/
44 -function SwitchController($element, $scope) { 44 +function SwitchController($element, $scope, types, utils) {
45 let vm = this; 45 let vm = this;
46 46
47 vm.showTitle = false; 47 vm.showTitle = false;
@@ -63,12 +63,20 @@ function SwitchController($element, $scope) { @@ -63,12 +63,20 @@ function SwitchController($element, $scope) {
63 63
64 vm.onValue = onValue; 64 vm.onValue = onValue;
65 65
  66 + vm.valueSubscription = null;
  67 +
66 $scope.$watch('vm.ctx', () => { 68 $scope.$watch('vm.ctx', () => {
67 if (vm.ctx) { 69 if (vm.ctx) {
68 init(); 70 init();
69 } 71 }
70 }); 72 });
71 73
  74 + $scope.$on('$destroy', () => {
  75 + if (vm.valueSubscription) {
  76 + vm.ctx.subscriptionApi.removeSubscription(vm.valueSubscription.id);
  77 + }
  78 + });
  79 +
72 function init() { 80 function init() {
73 81
74 vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : ''; 82 vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
@@ -90,6 +98,35 @@ function SwitchController($element, $scope) { @@ -90,6 +98,35 @@ function SwitchController($element, $scope) {
90 if (vm.ctx.settings.requestTimeout) { 98 if (vm.ctx.settings.requestTimeout) {
91 vm.requestTimeout = vm.ctx.settings.requestTimeout; 99 vm.requestTimeout = vm.ctx.settings.requestTimeout;
92 } 100 }
  101 +
  102 + vm.retrieveValueMethod = 'rpc';
  103 + if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) {
  104 + vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod;
  105 + }
  106 +
  107 + vm.valueKey = 'value';
  108 + if (vm.ctx.settings.valueKey && vm.ctx.settings.valueKey.length) {
  109 + vm.valueKey = vm.ctx.settings.valueKey;
  110 + }
  111 +
  112 + vm.parseValueFunction = (data) => data ? true : false;
  113 + if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) {
  114 + try {
  115 + vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction);
  116 + } catch (e) {
  117 + vm.parseValueFunction = (data) => data ? true : false;
  118 + }
  119 + }
  120 +
  121 + vm.convertValueFunction = (value) => value;
  122 + if (vm.ctx.settings.convertValueFunction && vm.ctx.settings.convertValueFunction.length) {
  123 + try {
  124 + vm.convertValueFunction = new Function('value', vm.ctx.settings.convertValueFunction);
  125 + } catch (e) {
  126 + vm.convertValueFunction = (value) => value;
  127 + }
  128 + }
  129 +
93 vm.getValueMethod = 'getValue'; 130 vm.getValueMethod = 'getValue';
94 if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { 131 if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
95 vm.getValueMethod = vm.ctx.settings.getValueMethod; 132 vm.getValueMethod = vm.ctx.settings.getValueMethod;
@@ -102,7 +139,11 @@ function SwitchController($element, $scope) { @@ -102,7 +139,11 @@ function SwitchController($element, $scope) {
102 onError('Target device is not set!'); 139 onError('Target device is not set!');
103 } else { 140 } else {
104 if (!vm.isSimulated) { 141 if (!vm.isSimulated) {
105 - rpcRequestValue(); 142 + if (vm.retrieveValueMethod == 'rpc') {
  143 + rpcRequestValue();
  144 + } else if (vm.retrieveValueMethod == 'attribute' || vm.retrieveValueMethod == 'timeseries') {
  145 + subscribeForValue();
  146 + }
106 } 147 }
107 } 148 }
108 } 149 }
@@ -144,6 +185,66 @@ function SwitchController($element, $scope) { @@ -144,6 +185,66 @@ function SwitchController($element, $scope) {
144 setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); 185 setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
145 } 186 }
146 187
  188 + function subscribeForValue() {
  189 + var valueSubscriptionInfo = [{
  190 + type: types.datasourceType.entity,
  191 + entityType: types.entityType.device,
  192 + entityId: vm.ctx.defaultSubscription.targetDeviceId
  193 + }];
  194 + if (vm.retrieveValueMethod == 'attribute') {
  195 + valueSubscriptionInfo[0].attributes = [
  196 + {name: vm.valueKey}
  197 + ];
  198 + } else {
  199 + valueSubscriptionInfo[0].timeseries = [
  200 + {name: vm.valueKey}
  201 + ];
  202 + }
  203 + var subscriptionOptions = {
  204 + callbacks: {
  205 + onDataUpdated: onDataUpdated,
  206 + onDataUpdateError: onDataUpdateError
  207 + }
  208 + };
  209 + vm.ctx.subscriptionApi.createSubscriptionFromInfo (
  210 + types.widgetType.latest.value, valueSubscriptionInfo, subscriptionOptions, false, true).then(
  211 + (subscription) => {
  212 + vm.valueSubscription = subscription;
  213 + }
  214 + );
  215 + }
  216 +
  217 + function onDataUpdated(subscription, apply) {
  218 + var value = false;
  219 + var data = subscription.data;
  220 + if (data.length) {
  221 + var keyData = data[0];
  222 + if (keyData && keyData.data && keyData.data[0]) {
  223 + var attrValue = keyData.data[0][1];
  224 + if (attrValue) {
  225 + var parsed = null;
  226 + try {
  227 + parsed = vm.parseValueFunction(angular.fromJson(attrValue));
  228 + } catch (e){/**/}
  229 + value = parsed ? true : false;
  230 + }
  231 + }
  232 + }
  233 + setValue(value);
  234 + if (apply) {
  235 + $scope.$digest();
  236 + }
  237 + }
  238 +
  239 + function onDataUpdateError(subscription, e) {
  240 + var exceptionData = utils.parseException(e);
  241 + var errorText = exceptionData.name;
  242 + if (exceptionData.message) {
  243 + errorText += ': ' + exceptionData.message;
  244 + }
  245 + onError(errorText);
  246 + }
  247 +
147 function setValue(value) { 248 function setValue(value) {
148 vm.value = value ? true : false; 249 vm.value = value ? true : false;
149 } 250 }
@@ -178,7 +279,7 @@ function SwitchController($element, $scope) { @@ -178,7 +279,7 @@ function SwitchController($element, $scope) {
178 vm.error = ''; 279 vm.error = '';
179 vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( 280 vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
180 (responseBody) => { 281 (responseBody) => {
181 - setValue(responseBody); 282 + setValue(vm.parseValueFunction(responseBody));
182 }, 283 },
183 () => { 284 () => {
184 var errorText = vm.ctx.defaultSubscription.rpcErrorText; 285 var errorText = vm.ctx.defaultSubscription.rpcErrorText;
@@ -197,7 +298,7 @@ function SwitchController($element, $scope) { @@ -197,7 +298,7 @@ function SwitchController($element, $scope) {
197 vm.executingUpdateValue = true; 298 vm.executingUpdateValue = true;
198 } 299 }
199 vm.error = ''; 300 vm.error = '';
200 - vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( 301 + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, vm.convertValueFunction(value), vm.requestTimeout).then(
201 () => { 302 () => {
202 vm.executingUpdateValue = false; 303 vm.executingUpdateValue = false;
203 if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) { 304 if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {