thingsboard-aplcache

Details

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 67a076b..6f4685c 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": "<tb-switch ctx='ctx'></tb-switch>",
         "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": "<tb-round-switch ctx='ctx'></tb-round-switch>",
         "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": "<tb-led-indicator ctx='ctx'></tb-led-indicator>",
         "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 61b3e8a..6ebe3c9 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 0487ee3..04615c7 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 edb8c7b..4f85ff2 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 b84b2f1..6256165 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) {