thingsboard-aplcache

Merge pull request #212 from thingsboard/feature/TB-70 TB-70:

7/20/2017 3:47:06 PM

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 b05a144..ce667af 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
@@ -48,9 +48,25 @@
         "templateHtml": "<tb-knob ctx='ctx'></tb-knob>",
         "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            \"minValue\": {\n                \"title\": \"Minimum value\",\n                \"type\": \"number\",\n                \"default\": 0\n            },\n            \"maxValue\": {\n                \"title\": \"Maximum value\",\n                \"type\": \"number\",\n                \"default\": 100\n            },\n            \"initialValue\": {\n                \"title\": \"Initial value\",\n                \"type\": \"number\",\n                \"default\": 50\n            },\n            \"title\": {\n                \"title\": \"Knob 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\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n    },\n    \"form\": [\n        \"minValue\",\n        \"maxValue\",\n        \"initialValue\",\n        \"getValueMethod\",\n        \"setValueMethod\",\n        \"title\",\n        \"requestTimeout\"\n    ]\n}",
+        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"Settings\",\n        \"properties\": {\n            \"minValue\": {\n                \"title\": \"Minimum value\",\n                \"type\": \"number\",\n                \"default\": 0\n            },\n            \"maxValue\": {\n                \"title\": \"Maximum value\",\n                \"type\": \"number\",\n                \"default\": 100\n            },\n            \"initialValue\": {\n                \"title\": \"Initial value\",\n                \"type\": \"number\",\n                \"default\": 50\n            },\n            \"title\": {\n                \"title\": \"Knob 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\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n    },\n    \"form\": [\n        \"minValue\",\n        \"maxValue\",\n        \"initialValue\",\n        \"title\",\n        \"getValueMethod\",\n        \"setValueMethod\",\n        \"requestTimeout\"\n    ]\n}",
         "dataKeySettingsSchema": "{}\n",
-        "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\"},\"title\":\"Knob Control\",\"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,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+      }
+    },
+    {
+      "alias": "switch_control",
+      "name": "Switch Control",
+      "descriptor": {
+        "type": "rpc",
+        "sizeX": 4,
+        "sizeY": 2.5,
+        "resources": [],
+        "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}",
+        "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}"
       }
     }
   ]
diff --git a/ui/src/app/widget/lib/rpc/index.js b/ui/src/app/widget/lib/rpc/index.js
index ba07eee..3edbdff 100644
--- a/ui/src/app/widget/lib/rpc/index.js
+++ b/ui/src/app/widget/lib/rpc/index.js
@@ -15,7 +15,9 @@
  */
 
 import tbKnob from './knob.directive';
+import tbSwitch from './switch.directive';
 
 export default angular.module('thingsboard.widgets.rpc', [
-    tbKnob
+    tbKnob,
+    tbSwitch
 ]).name;
diff --git a/ui/src/app/widget/lib/rpc/knob.directive.js b/ui/src/app/widget/lib/rpc/knob.directive.js
index d62d0fb..f921854 100644
--- a/ui/src/app/widget/lib/rpc/knob.directive.js
+++ b/ui/src/app/widget/lib/rpc/knob.directive.js
@@ -63,7 +63,7 @@ function KnobController($element, $scope, $document) {
         knobTitleContainer = knob.find('.title-container'),
         knobTitle = knob.find('.knob-title'),
         knobMinmaxContainer = knob.find('.minmax-container'),
-        minmaxLanel = knob.find('.minmax-label'),
+        minmaxLabel = knob.find('.minmax-label'),
         textMeasure = knob.find('#text-measure'),
         startDeg = -1,
         currentDeg = 0,
@@ -92,8 +92,6 @@ function KnobController($element, $scope, $document) {
         vm.maxValue = angular.isDefined(vm.ctx.settings.maxValue) ? vm.ctx.settings.maxValue : 100;
         vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
 
-        vm.darkTheme = vm.ctx.settings.theme == 'dark';
-
         var canvasBarData = {
             renderTo: canvasBarElement[0],
             hideValue: true,
@@ -267,7 +265,7 @@ function KnobController($element, $scope, $document) {
         setFontSize(knobTitle, vm.title, knobTitleContainer.height(), knobTitleContainer.width());
         setFontSize(knobError, vm.error, knobErrorContainer.height(), knobErrorContainer.width());
         var minmaxHeight = knobMinmaxContainer.height();
-        minmaxLanel.css({'fontSize': minmaxHeight+'px', 'lineHeight': minmaxHeight+'px'});
+        minmaxLabel.css({'fontSize': minmaxHeight+'px', 'lineHeight': minmaxHeight+'px'});
         checkValueSize();
     }
 
diff --git a/ui/src/app/widget/lib/rpc/knob.scss b/ui/src/app/widget/lib/rpc/knob.scss
index 3e57f66..afd9077 100644
--- a/ui/src/app/widget/lib/rpc/knob.scss
+++ b/ui/src/app/widget/lib/rpc/knob.scss
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-$knob-img: url('./knob.svg');
+$knob-img: url('./svg/knob.svg');
 
 $bars-margin-pct: percentage(0.033);
 $background-margin-pct: percentage(0.05);
@@ -33,9 +33,6 @@ $background-color: #e6e7e8;
   width:100%;
   height:100%;
   background: $background-color;
-  &.dark {
-    background: #000;
-  }
 
   .knob {
     position: relative;
diff --git a/ui/src/app/widget/lib/rpc/knob.tpl.html b/ui/src/app/widget/lib/rpc/knob.tpl.html
index 93276b4..ffd670f 100644
--- a/ui/src/app/widget/lib/rpc/knob.tpl.html
+++ b/ui/src/app/widget/lib/rpc/knob.tpl.html
@@ -16,7 +16,7 @@
 
 -->
 
-<div class="tb-knob" layout="column" ng-class="{'dark': vm.darkTheme}" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
+<div class="tb-knob" layout="column" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
     <div id="knob-container" flex layout="column" layout-align="center center">
         <div class="knob">
             <div class="value-container" layout="row" layout-align="center center">
diff --git a/ui/src/app/widget/lib/rpc/svg/thumb.svg b/ui/src/app/widget/lib/rpc/svg/thumb.svg
new file mode 100644
index 0000000..03e9ea0
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/svg/thumb.svg
@@ -0,0 +1,37 @@
+<svg id="svg6201" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="7.6161mm" width="11.594mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 41.08074 26.986349" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs6203">
+  <linearGradient id="linearGradient5382" y2="-412.79" gradientUnits="userSpaceOnUse" x2="821.92" gradientTransform="matrix(1 0 0 1.0219 -4.8132 52.955)" y1="-386.98" x1="822.46">
+   <stop id="stop5264" stop-color="#3d3d3d" offset="0"/>
+   <stop id="stop5266" stop-color="#fbfbfb" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient5384" y2="-350.07" gradientUnits="userSpaceOnUse" x2="821.71" gradientTransform="translate(-3.8933 5.8759)" y1="-372.35" x1="821.89">
+   <stop id="stop5239" stop-color="#d1d1d1" offset="0"/>
+   <stop id="stop5243" stop-color="#e2e3e2" offset=".36037"/>
+   <stop id="stop5245" stop-color="#ededed" offset=".67781"/>
+   <stop id="stop5241" stop-color="#fcfcfc" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient5644" y2="-333.89" gradientUnits="userSpaceOnUse" x2="913.16" gradientTransform="matrix(.78723 0 0 .78723 105.52 -77.537)" y1="-348.85" x1="912.9">
+   <stop id="stop5650" stop-color="#939393" offset="0"/>
+   <stop id="stop5652" stop-color="#fff" offset="1"/>
+  </linearGradient>
+  <radialGradient id="radialGradient5502-1-0" gradientUnits="userSpaceOnUse" cy="-342.2" cx="916.19" gradientTransform="matrix(1.9993 -.036196 .028926 1.5978 -997.39 230.16)" r="7.5588">
+   <stop id="stop5656" stop-color="#fff" stop-opacity="0" offset="0"/>
+   <stop id="stop5658" stop-color="#fff" stop-opacity=".18551" offset=".20750"/>
+   <stop id="stop5660" stop-color="#fff" stop-opacity=".32464" offset=".31814"/>
+   <stop id="stop5662" stop-color="#fff" offset="1"/>
+  </radialGradient>
+ </defs>
+ <g id="layer1" transform="translate(-288.03 -544.58)">
+  <g id="g5664" transform="translate(-515.63 904.29)">
+   <g id="g5329-3" transform="translate(13.694 -2.3675)">
+    <path id="rect5195-5-0-8" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m797.58-357.34a7.6067 7.8088 0 0 0 -7.6055 7.8098v11.369a7.6067 7.8088 0 0 0 7.6055 7.8076h25.87a7.6067 7.8088 0 0 0 7.6055 -7.8076v-11.369a7.6067 7.8088 0 0 0 -7.6055 -7.8098h-25.87z" fill="#1b1b1b"/>
+    <g id="g5300-6" transform="translate(-6.6863 11.667)">
+     <path id="rect5195-5-03" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m804.57-368.63a7.429 7.5918 0 0 0 -7.4279 7.5927v11.053a7.429 7.5918 0 0 0 7.4279 7.5906h25.266a7.429 7.5918 0 0 0 7.4279 -7.5906v-11.053a7.429 7.5918 0 0 0 -7.4279 -7.5927h-25.266z" fill="url(#linearGradient5382)"/>
+     <path id="rect5195-3" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m805.23-367.67a7.0362 7.0362 0 0 0 -7.0352 7.0371v10.244a7.0362 7.0362 0 0 0 7.0352 7.0352h23.93a7.0362 7.0362 0 0 0 7.0352 -7.0352v-10.244a7.0362 7.0362 0 0 0 -7.0352 -7.0371h-23.93z" fill="url(#linearGradient5384)"/>
+    </g>
+   </g>
+   <circle id="path5494-3-9-7" cy="-346.21" cx="824.21" r="6.0124" fill="url(#linearGradient5644)"/>
+   <circle id="path5494-7-8" cy="-346.21" cx="824.21" r="5.3939" fill="url(#radialGradient5502-1-0)"/>
+  </g>
+ </g>
+</svg>
diff --git a/ui/src/app/widget/lib/rpc/svg/thumb-bar.svg b/ui/src/app/widget/lib/rpc/svg/thumb-bar.svg
new file mode 100644
index 0000000..0b95293
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/svg/thumb-bar.svg
@@ -0,0 +1,42 @@
+<svg id="svg4749" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="8.4667mm" width="23.616mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 83.679981 29.999998" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs4751">
+  <linearGradient id="linearGradient5443" y2="-327.83" gradientUnits="userSpaceOnUse" x2="895.4" y1="-357.17" x1="895.4">
+   <stop id="stop5439" stop-color="#bdbdc1" offset="0"/>
+   <stop id="stop5441" stop-color="#f4f4f4" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient5490" y2="-392.51" gradientUnits="userSpaceOnUse" x2="892.44" gradientTransform="matrix(1 0 0 .78494 -2.6922 -42.464)" y1="-366.35" x1="892.44">
+   <stop id="stop5486" stop-color="#838383" offset="0"/>
+   <stop id="stop5492" stop-color="#979797" offset=".53774"/>
+   <stop id="stop5488" stop-color="#a7a7a7" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient5534" y2="-334.29" xlink:href="#linearGradient5528" gradientUnits="userSpaceOnUse" x2="913.4" gradientTransform="matrix(.78723 0 0 .78723 192.99 -72.172)" y1="-349.49" x1="913.38"/>
+  <linearGradient id="linearGradient5528">
+   <stop id="stop5530" stop-color="#3d3d3d" stop-opacity=".86275" offset="0"/>
+   <stop id="stop5532" stop-color="#fff" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <radialGradient id="radialGradient5502" xlink:href="#linearGradient5496" gradientUnits="userSpaceOnUse" cy="-341.87" cx="916.16" gradientTransform="matrix(4.5735 -.17574 .13786 3.5876 -3230.9 1043.7)" r="7.5588"/>
+  <linearGradient id="linearGradient5496">
+   <stop id="stop5498" stop-color="#fff" stop-opacity="0" offset="0"/>
+   <stop id="stop5550" stop-color="#fff" stop-opacity=".10435" offset=".17494"/>
+   <stop id="stop5552" stop-color="#fff" stop-opacity=".92174" offset=".39279"/>
+   <stop id="stop5500" stop-color="#fff" offset="1"/>
+  </linearGradient>
+ </defs>
+ <g id="layer1" transform="translate(-312.45 -714.51)">
+  <g id="g5878" transform="translate(-538.52 1072.5)">
+   <g>
+    <path id="rect5195-5-0-1" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m858.57-358.03c-4.201 0.00074-7.606 3.8877-7.6055 8.6819v12.639c0.00065 4.7933 3.4054 8.6788 7.6055 8.6795h68.469c4.2001-0.00073 7.6049-3.8862 7.6055-8.6795v-12.639c0.00053-4.7942-3.4046-8.6812-7.6055-8.6819z" fill="url(#linearGradient5443)"/>
+    <path id="rect5195-5-0-1-4" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m858.57-355.25c-4.201 0.00062-7.606 3.2632-7.6055 7.2872v10.608c0.00065 4.0232 3.4054 7.2845 7.6055 7.2851h68.469c4.2001-0.00062 7.6049-3.2619 7.6055-7.2851v-10.608c0.00053-4.024-3.4046-7.2866-7.6055-7.2872z" fill="#585859"/>
+    <path id="rect5195-5-0-1-4-3" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m858.57-351.49c-4.201 0.00052-7.606 2.7592-7.6055 6.1617v8.9697c0.00065 3.4018 3.4054 6.1594 7.6055 6.16h68.469c4.2001-0.00052 7.6049-2.7581 7.6055-6.16v-8.9697c0.00053-3.4025-3.4046-6.1612-7.6055-6.1617z" fill="url(#linearGradient5490)"/>
+   </g>
+   <g id="g5554" transform="translate(2.0884 -2.1835)">
+    <circle id="path5494-3" cx="911.68" cy="-340.85" r="6.0124" fill="url(#linearGradient5534)"/>
+    <circle id="path5494" cx="911.68" cy="-340.85" r="5.3939" fill="url(#radialGradient5502)"/>
+   </g>
+   <g id="g5554-8" transform="translate(-39.746 -2.1835)">
+    <circle id="path5494-3-9" cx="911.68" cy="-340.85" r="6.0124" fill="url(#linearGradient5534)"/>
+    <circle id="path5494-7" cx="911.68" cy="-340.85" r="5.3939" fill="url(#radialGradient5502)"/>
+   </g>
+  </g>
+ </g>
+</svg>
diff --git a/ui/src/app/widget/lib/rpc/svg/thumb-bar-checked.svg b/ui/src/app/widget/lib/rpc/svg/thumb-bar-checked.svg
new file mode 100644
index 0000000..57fa2b4
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/svg/thumb-bar-checked.svg
@@ -0,0 +1,43 @@
+<svg id="svg5466" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="8.4667mm" width="23.616mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 83.679981 29.999998" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs5468">
+  <linearGradient id="linearGradient6001-5" y2="-327.83" gradientUnits="userSpaceOnUse" x2="895.4" gradientTransform="translate(-31.905 97.852)" y1="-357.17" x1="895.4">
+   <stop id="stop5439" stop-color="#bdbdc1" offset="0"/>
+   <stop id="stop5441" stop-color="#f4f4f4" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient6003-6" y2="-392.51" gradientUnits="userSpaceOnUse" x2="892.44" gradientTransform="matrix(1 0 0 .78494 -34.597 55.388)" y1="-366.35" x1="892.44">
+   <stop id="stop5486" stop-color="#838383" offset="0"/>
+   <stop id="stop5492" stop-color="#979797" offset=".53774"/>
+   <stop id="stop5488" stop-color="#a7a7a7" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient6436" y2="-334.29" xlink:href="#linearGradient5528" gradientUnits="userSpaceOnUse" x2="913.4" gradientTransform="matrix(.78723 0 0 .78723 192.99 -72.172)" y1="-349.49" x1="913.38"/>
+  <linearGradient id="linearGradient5528">
+   <stop id="stop5530" stop-color="#3d3d3d" stop-opacity=".86275" offset="0"/>
+   <stop id="stop5532" stop-color="#fff" stop-opacity="0" offset="1"/>
+  </linearGradient>
+  <radialGradient id="radialGradient5502-17-9" xlink:href="#linearGradient5496" gradientUnits="userSpaceOnUse" cy="-341.87" cx="916.16" gradientTransform="matrix(4.5735 -.17574 .13786 3.5876 -3230.9 1043.7)" r="7.5588"/>
+  <linearGradient id="linearGradient5496">
+   <stop id="stop5498" stop-color="#fff" stop-opacity="0" offset="0"/>
+   <stop id="stop5550" stop-color="#fff" stop-opacity=".10435" offset=".17494"/>
+   <stop id="stop5552" stop-color="#fff" stop-opacity=".92174" offset=".39279"/>
+   <stop id="stop5500" stop-color="#fff" offset="1"/>
+  </linearGradient>
+ </defs>
+ <g id="layer1" transform="translate(-126.73 -454.51)">
+  <g id="g6480" transform="translate(-692.33 714.68)">
+   <g>
+    <path id="rect5195-5-0-1-9-6" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m826.67-260.18c-4.201 0.00074-7.606 3.8877-7.6055 8.6819v12.639c0.00065 4.7933 3.4054 8.6788 7.6055 8.6795h68.469c4.2001-0.00073 7.6049-3.8862 7.6055-8.6795v-12.639c0.00053-4.7942-3.4046-8.6812-7.6055-8.6819z" fill="url(#linearGradient6001-5)"/>
+    <path id="rect5195-5-0-1-4-0-6" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m826.67-257.4c-4.201 0.00062-7.606 3.2632-7.6055 7.2872v10.608c0.00065 4.0232 3.4054 7.2845 7.6055 7.2851h68.469c4.2001-0.00062 7.6049-3.2619 7.6055-7.2851v-10.608c0.00053-4.024-3.4046-7.2866-7.6055-7.2872z" fill="#585859"/>
+    <path id="rect5195-5-0-1-4-3-0-8" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m826.67-253.64c-4.201 0.00052-7.606 2.7592-7.6055 6.1617v8.9697c0.00065 3.4018 3.4054 6.1594 7.6055 6.16h68.469c4.2001-0.00052 7.6049-2.7581 7.6055-6.16v-8.9697c0.00053-3.4025-3.4046-6.1612-7.6055-6.1617z" fill="url(#linearGradient6003-6)"/>
+   </g>
+   <g id="g5554-2-1" transform="translate(-29.817 95.669)">
+    <circle id="path5494-3-7-8" cx="911.68" cy="-340.85" r="6.0124" fill="url(#linearGradient6436)"/>
+    <circle id="path5494-1-9" cx="911.68" cy="-340.85" r="5.3939" fill="url(#radialGradient5502-17-9)"/>
+   </g>
+   <g id="g5554-8-6-2" transform="translate(-71.652 95.669)">
+    <circle id="path5494-3-9-2-9" cx="911.68" cy="-340.85" r="6.0124" fill="url(#linearGradient6436)"/>
+    <circle id="path5494-7-0-5" cx="911.68" cy="-340.85" r="5.3939" fill="url(#radialGradient5502-17-9)"/>
+   </g>
+   <path id="rect5195-5-0-1-4-0-4-4" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m826.67-257.4c-4.201 0.00062-7.606 3.2632-7.6055 7.2872v10.608c0.00065 4.0232 3.4054 7.2845 7.6055 7.2851h68.469c4.2001-0.00062 7.6049-3.2619 7.6055-7.2851v-10.608c0.00053-4.024-3.4046-7.2866-7.6055-7.2872z" fill-opacity=".39080" fill="#ff6e4a"/>
+  </g>
+ </g>
+</svg>
diff --git a/ui/src/app/widget/lib/rpc/svg/thumb-checked.svg b/ui/src/app/widget/lib/rpc/svg/thumb-checked.svg
new file mode 100644
index 0000000..6d59b94
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/svg/thumb-checked.svg
@@ -0,0 +1,40 @@
+<svg id="svg7523" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="7.6161mm" width="11.594mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 41.08074 26.986356" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs7525">
+  <linearGradient id="linearGradient6257" y2="-412.79" gradientUnits="userSpaceOnUse" x2="821.92" gradientTransform="matrix(1 0 0 1.0219 -4.8132 52.955)" y1="-386.98" x1="822.46">
+   <stop id="stop5264" stop-color="#3d3d3d" offset="0"/>
+   <stop id="stop5266" stop-color="#fbfbfb" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient6259" y2="-350.07" gradientUnits="userSpaceOnUse" x2="821.71" gradientTransform="translate(-3.8933 5.8759)" y1="-372.35" x1="821.89">
+   <stop id="stop5239" stop-color="#d1d1d1" offset="0"/>
+   <stop id="stop5243" stop-color="#e2e3e2" offset=".36037"/>
+   <stop id="stop5245" stop-color="#ededed" offset=".67781"/>
+   <stop id="stop5241" stop-color="#fcfcfc" offset="1"/>
+  </linearGradient>
+  <linearGradient id="linearGradient6261" y2="-333.89" gradientUnits="userSpaceOnUse" x2="913.16" gradientTransform="matrix(.78723 0 0 .78723 105.52 -77.537)" y1="-348.85" x1="912.9">
+   <stop id="stop5650" stop-color="#939393" offset="0"/>
+   <stop id="stop5652" stop-color="#fff" offset="1"/>
+  </linearGradient>
+  <radialGradient id="radialGradient5502-1-0-6" gradientUnits="userSpaceOnUse" cy="-342.2" cx="916.19" gradientTransform="matrix(1.9993 -.036196 .028926 1.5978 -997.39 230.16)" r="7.5588">
+   <stop id="stop5656" stop-color="#fff" stop-opacity="0" offset="0"/>
+   <stop id="stop5658" stop-color="#fff" stop-opacity=".18551" offset=".20750"/>
+   <stop id="stop5660" stop-color="#fff" stop-opacity=".32464" offset=".31814"/>
+   <stop id="stop5662" stop-color="#fff" offset="1"/>
+  </radialGradient>
+ </defs>
+ <g id="layer1" transform="translate(-259.46 -610.3)">
+  <g id="g6501" transform="translate(-602.12 869.32)">
+   <g id="g5664-5" transform="translate(57.91 100.68)">
+    <g id="g5329-3-4" transform="translate(13.694 -2.3675)">
+     <path id="rect5195-5-0-8-35" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m797.58-357.34a7.6067 7.8088 0 0 0 -7.6055 7.8098v11.369a7.6067 7.8088 0 0 0 7.6055 7.8076h25.87a7.6067 7.8088 0 0 0 7.6055 -7.8076v-11.369a7.6067 7.8088 0 0 0 -7.6055 -7.8098h-25.87z" fill="#1b1b1b"/>
+     <g id="g5300-6-7" transform="translate(-6.6863 11.667)">
+      <path id="rect5195-5-03-5" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m804.57-368.63a7.429 7.5918 0 0 0 -7.4279 7.5927v11.053a7.429 7.5918 0 0 0 7.4279 7.5906h25.266a7.429 7.5918 0 0 0 7.4279 -7.5906v-11.053a7.429 7.5918 0 0 0 -7.4279 -7.5927h-25.266z" fill="url(#linearGradient6257)"/>
+      <path id="rect5195-3-6" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m805.23-367.67a7.0362 7.0362 0 0 0 -7.0352 7.0371v10.244a7.0362 7.0362 0 0 0 7.0352 7.0352h23.93a7.0362 7.0362 0 0 0 7.0352 -7.0352v-10.244a7.0362 7.0362 0 0 0 -7.0352 -7.0371h-23.93z" fill="url(#linearGradient6259)"/>
+     </g>
+    </g>
+    <circle id="path5494-3-9-7-5" cx="824.21" cy="-346.21" r="6.0124" fill="url(#linearGradient6261)"/>
+    <circle id="path5494-7-8-95" cx="824.21" cy="-346.21" r="5.3939" fill="url(#radialGradient5502-1-0-6)"/>
+   </g>
+   <path id="rect5195-5-0-8-3-8" style="color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;text-decoration-style:solid;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none" d="m869.18-259.03a7.6067 7.8088 0 0 0 -7.6055 7.8098v11.369a7.6067 7.8088 0 0 0 7.6055 7.8076h25.87a7.6067 7.8088 0 0 0 7.6055 -7.8076v-11.369a7.6067 7.8088 0 0 0 -7.6055 -7.8098h-25.87z" fill-opacity=".78448" fill="#ff6e40"/>
+  </g>
+ </g>
+</svg>
diff --git a/ui/src/app/widget/lib/rpc/switch.directive.js b/ui/src/app/widget/lib/rpc/switch.directive.js
new file mode 100644
index 0000000..b84b2f1
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/switch.directive.js
@@ -0,0 +1,214 @@
+/*
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import './switch.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import switchTemplate from './switch.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+export default angular.module('thingsboard.widgets.rpc.switch', [])
+    .directive('tbSwitch', Switch)
+    .name;
+
+/*@ngInject*/
+function Switch() {
+    return {
+        restrict: "E",
+        scope: true,
+        bindToController: {
+            ctx: '='
+        },
+        controller: SwitchController,
+        controllerAs: 'vm',
+        templateUrl: switchTemplate
+    };
+}
+
+/*@ngInject*/
+function SwitchController($element, $scope) {
+    let vm = this;
+
+    vm.showTitle = false;
+    vm.value = false;
+    vm.error = '';
+
+    var switchElement = angular.element('.switch', $element),
+        switchContainer = angular.element('#switch-container', $element),
+        mdSwitch = angular.element('md-switch', switchElement),
+        onoffContainer = angular.element('.onoff-container', $element),
+        onLabel = angular.element('.on-label', $element),
+        offLabel = angular.element('.off-label', $element),
+        switchTitleContainer = angular.element('.title-container', $element),
+        switchTitle = angular.element('.switch-title', $element),
+        textMeasure = angular.element('#text-measure', $element),
+        switchErrorContainer = angular.element('.error-container', $element),
+        switchError = angular.element('.switch-error', $element);
+
+
+    vm.onValue = onValue;
+
+    $scope.$watch('vm.ctx', () => {
+        if (vm.ctx) {
+            init();
+        }
+    });
+
+    function init() {
+
+        vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
+        vm.showTitle = vm.title && vm.title.length ? true : false;
+        vm.showOnOffLabels = angular.isDefined(vm.ctx.settings.showOnOffLabels) ? vm.ctx.settings.showOnOffLabels : true;
+        vm.ctx.resize = resize;
+        $scope.$applyAsync(() => {
+            resize();
+        });
+        var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : false;
+        setValue(initialValue);
+
+        var subscription = vm.ctx.defaultSubscription;
+        var rpcEnabled = subscription.rpcEnabled;
+
+        vm.isSimulated = $scope.widgetEditMode;
+
+        vm.requestTimeout = 500;
+        if (vm.ctx.settings.requestTimeout) {
+            vm.requestTimeout = vm.ctx.settings.requestTimeout;
+        }
+        vm.getValueMethod = 'getValue';
+        if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
+            vm.getValueMethod = vm.ctx.settings.getValueMethod;
+        }
+        vm.setValueMethod = 'setValue';
+        if (vm.ctx.settings.setValueMethod && vm.ctx.settings.setValueMethod.length) {
+            vm.setValueMethod = vm.ctx.settings.setValueMethod;
+        }
+        if (!rpcEnabled) {
+            onError('Target device is not set!');
+        } else {
+            if (!vm.isSimulated) {
+                rpcRequestValue();
+            }
+        }
+    }
+
+    const switchAspectRation = 2.7893;
+
+    function resize() {
+        var width = switchContainer.width();
+        var height;
+        if (vm.showOnOffLabels) {
+            height = switchContainer.height()*2/3;
+        } else {
+            height = switchContainer.height();
+        }
+        var ratio = width/height;
+        if (ratio > switchAspectRation) {
+            width = height*switchAspectRation;
+        } else {
+            height = width/switchAspectRation;
+        }
+        switchElement.css({width: width, height: height});
+        mdSwitch.css('height', height+'px');
+        mdSwitch.css('width', width+'px');
+        mdSwitch.css('min-width', width+'px');
+        angular.element('.md-container', mdSwitch).css('height', height+'px');
+        angular.element('.md-container', mdSwitch).css('width', width+'px');
+
+
+        if (vm.showTitle) {
+            setFontSize(switchTitle, vm.title, switchTitleContainer.height() * 2 / 3, switchTitleContainer.width());
+        }
+
+        if (vm.showOnOffLabels) {
+            onoffContainer.css({width: width, height: switchContainer.height() / 3});
+            setFontSize(onLabel, 'OFF', onoffContainer.height(), onoffContainer.width() / 2);
+            setFontSize(offLabel, 'OFF', onoffContainer.height(), onoffContainer.width() / 2);
+        }
+
+        setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
+    }
+
+    function setValue(value) {
+        vm.value = value ? true : false;
+    }
+
+    function onValue() {
+        rpcUpdateValue(vm.value);
+    }
+
+    function onError(error) {
+        $scope.$applyAsync(() => {
+            vm.error = error;
+            setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
+        });
+    }
+
+    function setFontSize(element, text, fontSize, maxWidth) {
+        var textWidth = measureTextWidth(text, fontSize);
+        while (textWidth > maxWidth) {
+            fontSize--;
+            textWidth = measureTextWidth(text, fontSize);
+        }
+        element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
+    }
+
+    function measureTextWidth(text, fontSize) {
+        textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
+        textMeasure.text(text);
+        return textMeasure.width();
+    }
+
+    function rpcRequestValue() {
+        vm.error = '';
+        vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
+            (responseBody) => {
+                setValue(responseBody);
+            },
+            () => {
+                var errorText = vm.ctx.defaultSubscription.rpcErrorText;
+                onError(errorText);
+            }
+        );
+    }
+
+    function rpcUpdateValue(value) {
+        if (vm.executingUpdateValue) {
+            vm.scheduledValue = value;
+            return;
+        } else {
+            vm.scheduledValue = null;
+            vm.rpcValue = value;
+            vm.executingUpdateValue = true;
+        }
+        vm.error = '';
+        vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then(
+            () => {
+                vm.executingUpdateValue = false;
+                if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {
+                    rpcUpdateValue(vm.scheduledValue);
+                }
+            },
+            () => {
+                vm.executingUpdateValue = false;
+                var errorText = vm.ctx.defaultSubscription.rpcErrorText;
+                onError(errorText);
+            }
+        );
+    }
+}
diff --git a/ui/src/app/widget/lib/rpc/switch.scss b/ui/src/app/widget/lib/rpc/switch.scss
new file mode 100644
index 0000000..9d69632
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/switch.scss
@@ -0,0 +1,127 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+$thumb-img: url('./svg/thumb.svg');
+$thumb-checked-img: url('./svg/thumb-checked.svg');
+$thumb-bar-img: url('./svg/thumb-bar.svg');
+$thumb-bar-checked-img: url('./svg/thumb-bar-checked.svg');
+
+$background-color: #e6e7e8;
+
+$error-height: 14px;
+
+.tb-switch {
+  width:100%;
+  height:100%;
+  background: $background-color;
+
+  .error-container {
+    position:absolute;
+    top: 1%;
+    left: 0;
+    right: 0;
+    z-index:4;
+    height: $error-height;
+    .switch-error {
+      color: #ff3315;
+      white-space: nowrap;
+    }
+  }
+
+  .onoff-container {
+    height: 100%;
+    color: #757575;
+    font-weight: 500;
+    white-space: nowrap;
+    .off-label {
+      color: #b7b5b5;
+    }
+    .on-label {
+      color: #ff7e57;
+      text-shadow: #ff6e4a 1px 1px 10px, #ffd1c3 1px 1px 10px;
+    }
+  }
+  .title-container {
+    .switch-title {
+      color: #757575;
+      font-weight: 500;
+      white-space: nowrap;
+    }
+  }
+
+  #switch-container {
+     padding-left: 10px;
+     padding-right: 10px;
+  }
+  .switch {
+    position: relative;
+    md-switch {
+      margin: 0;
+      position:absolute;
+      top: 0;
+      left: 0;
+      bottom: 0;
+      right: 0;
+      .md-container {
+        margin: 0;
+      }
+      .md-bar {
+        left: 0;
+        width: 100%;
+        top: 0;
+        height: 100%;
+        border-radius: 0;
+        background:$thumb-bar-img no-repeat;
+        background-size: contain;
+      }
+      .md-thumb-container {
+        left: 0.25%;
+        width: 50%;
+        top: 5%;
+        height: 90%;
+      }
+      .md-thumb {
+        top: 0;
+        left: 0;
+        height: 100%;
+        width: 100%;
+        background:$thumb-img no-repeat;
+        background-size: contain;
+        border-radius: 0;
+        box-shadow: none;
+      }
+
+      &.md-checked {
+        .md-bar {
+          background:$thumb-bar-checked-img no-repeat;
+          background-size: contain;
+        }
+        .md-thumb {
+          background:$thumb-checked-img no-repeat;
+          background-size: contain;
+        }
+      }
+
+    }
+  }
+  #text-measure {
+    position: absolute;
+    visibility: hidden;
+    height: auto;
+    width: auto;
+    white-space: nowrap;
+  }
+}
diff --git a/ui/src/app/widget/lib/rpc/switch.tpl.html b/ui/src/app/widget/lib/rpc/switch.tpl.html
new file mode 100644
index 0000000..387b159
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/switch.tpl.html
@@ -0,0 +1,40 @@
+<!--
+
+    Copyright © 2016-2017 The Thingsboard Authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<div class="tb-switch" layout="column" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
+    <div class="error-container" ng-style="{'background': vm.error.length ? 'rgba(255,255,255,0.25)' : 'none'}"
+         layout="row" layout-align="center center">
+        <span class="switch-error">{{ vm.error }}</span>
+    </div>
+    <div flex="30" class="title-container" layout="row" layout-align="center center" ng-show="vm.showTitle">
+        <span class="switch-title">{{vm.title}}</span>
+    </div>
+    <div flex="{{vm.showTitle ? 70 : 100}}" id="switch-container" layout="column" layout-align="center center">
+        <div class="switch">
+            <md-switch md-no-ink aria-label="Switch control" ng-model="vm.value" ng-change="vm.onValue()">
+            </md-switch>
+        </div>
+        <div class="onoff-container" layout="row" layout-align="center start" ng-show="vm.showOnOffLabels">
+            <span flex class="off-label" ng-show="!vm.value" style="text-align: center;">OFF</span>
+            <span flex ng-show="vm.value"></span>
+            <span flex class="on-label" ng-show="vm.value" style="text-align: center;">ON</span>
+            <span flex ng-show="!vm.value"></span>
+        </div>
+    </div>
+    <div id="text-measure"></div>
+</div>