thingsboard-aplcache
Changes
ui/src/app/widget/lib/rpc/index.js 6(+5 -1)
ui/src/app/widget/lib/rpc/led-indicator.directive.js 201(+201 -0)
ui/src/app/widget/lib/rpc/led-indicator.scss 78(+78 -0)
ui/src/app/widget/lib/rpc/round-switch.directive.js 198(+198 -0)
ui/src/app/widget/lib/rpc/round-switch.scss 195(+195 -0)
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 ce667af..7f9c75a 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
@@ -68,6 +68,38 @@
"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}"
}
+ },
+ {
+ "alias": "round_switch",
+ "name": "Round switch",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 2.5,
+ "sizeY": 2,
+ "resources": [],
+ "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}",
+ "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}"
+ }
+ },
+ {
+ "alias": "led_indicator",
+ "name": "Led indicator",
+ "descriptor": {
+ "type": "rpc",
+ "sizeX": 2.5,
+ "sizeY": 2.5,
+ "resources": [],
+ "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 \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"valuePollingInterval\": {\n \"title\": \"Value polling interval (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"getValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"getValueMethod\",\n \"valuePollingInterval\",\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,\"getValueMethod\":\"getValue\",\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valuePollingInterval\":500},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
+ }
}
]
}
\ No newline at end of file
ui/src/app/widget/lib/rpc/index.js 6(+5 -1)
diff --git a/ui/src/app/widget/lib/rpc/index.js b/ui/src/app/widget/lib/rpc/index.js
index 3edbdff..d57878d 100644
--- a/ui/src/app/widget/lib/rpc/index.js
+++ b/ui/src/app/widget/lib/rpc/index.js
@@ -16,8 +16,12 @@
import tbKnob from './knob.directive';
import tbSwitch from './switch.directive';
+import tbRoundSwitch from './round-switch.directive';
+import tbLedIndicator from './led-indicator.directive';
export default angular.module('thingsboard.widgets.rpc', [
tbKnob,
- tbSwitch
+ tbSwitch,
+ tbRoundSwitch,
+ tbLedIndicator
]).name;
ui/src/app/widget/lib/rpc/led-indicator.directive.js 201(+201 -0)
diff --git a/ui/src/app/widget/lib/rpc/led-indicator.directive.js b/ui/src/app/widget/lib/rpc/led-indicator.directive.js
new file mode 100644
index 0000000..58a7c4b
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/led-indicator.directive.js
@@ -0,0 +1,201 @@
+/*
+ * 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 './led-indicator.scss';
+
+import tinycolor from 'tinycolor2';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import ledIndicatorTemplate from './led-indicator.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+export default angular.module('thingsboard.widgets.rpc.ledIndicator', [])
+ .directive('tbLedIndicator', LedIndicator)
+ .name;
+
+/*@ngInject*/
+function LedIndicator() {
+ return {
+ restrict: "E",
+ scope: true,
+ bindToController: {
+ ctx: '='
+ },
+ controller: LedIndicatorController,
+ controllerAs: 'vm',
+ templateUrl: ledIndicatorTemplate
+ };
+}
+
+/*@ngInject*/
+function LedIndicatorController($element, $scope, $timeout) {
+ let vm = this;
+
+ vm.showTitle = false;
+ vm.value = false;
+ vm.error = '';
+
+ var led = angular.element('.led', $element),
+ ledContainer = angular.element('#led-container', $element),
+ textMeasure = angular.element('#text-measure', $element),
+ ledTitleContainer = angular.element('.title-container', $element),
+ ledTitle = angular.element('.led-title', $element),
+ ledErrorContainer = angular.element('.error-container', $element),
+ ledError = angular.element('.led-error', $element);
+
+ $scope.$watch('vm.ctx', () => {
+ if (vm.ctx) {
+ init();
+ }
+ });
+
+ $scope.$on('$destroy', () => {
+ vm.destroyed = true;
+ if (vm.requestValueTimeoutHandle) {
+ $timeout.cancel(vm.requestValueTimeoutHandle);
+ }
+ });
+
+ resize();
+
+ function init() {
+
+ vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
+ vm.showTitle = vm.title && vm.title.length ? true : false;
+
+ var origColor = angular.isDefined(vm.ctx.settings.ledColor) ? vm.ctx.settings.ledColor : 'green';
+
+ vm.ledColor = tinycolor(origColor).brighten(30).toHexString();
+ vm.ledMiddleColor = tinycolor(origColor).toHexString();
+ vm.disabledColor = tinycolor(origColor).darken(40).toHexString();
+ vm.disabledMiddleColor = tinycolor(origColor).darken(60).toHexString();
+
+ vm.ctx.resize = resize;
+ $scope.$applyAsync(() => {
+ resize();
+ });
+ var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : false;
+ setValue(initialValue, true);
+
+ 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.valuePollingInterval = 500;
+ if (vm.ctx.settings.valuePollingInterval) {
+ vm.valuePollingInterval = vm.ctx.settings.valuePollingInterval;
+ }
+ vm.getValueMethod = 'getValue';
+ if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
+ vm.getValueMethod = vm.ctx.settings.getValueMethod;
+ }
+ if (!rpcEnabled) {
+ onError('Target device is not set!');
+ } else {
+ if (!vm.isSimulated) {
+ rpcRequestValue();
+ }
+ }
+ }
+
+ function resize() {
+ var width = ledContainer.width();
+ var height = ledContainer.height();
+ var size = Math.min(width, height);
+
+ led.css({width: size, height: size});
+
+ if (vm.showTitle) {
+ setFontSize(ledTitle, vm.title, ledTitleContainer.height() * 2 / 3, ledTitleContainer.width());
+ }
+ setFontSize(ledError, vm.error, ledErrorContainer.height(), ledErrorContainer.width());
+ }
+
+ function setValue(value, forceUpdate) {
+ if (vm.value != value || forceUpdate) {
+ vm.value = value;
+ updateColor();
+ }
+ }
+
+ function updateColor() {
+ var color = vm.value ? vm.ledColor : vm.disabledColor;
+ var middleColor = vm.value ? vm.ledMiddleColor : vm.disabledMiddleColor;
+ var boxShadow = `#000 0 -1px 6px 1px, inset ${middleColor} 0 -1px 8px, ${color} 0 3px 11px`;
+ led.css({'backgroundColor': color});
+ led.css({'boxShadow': boxShadow});
+ if (vm.value) {
+ led.removeClass( 'disabled' );
+ } else {
+ led.addClass( 'disabled' );
+ }
+ }
+
+ function onError(error) {
+ $scope.$applyAsync(() => {
+ vm.error = error;
+ setFontSize(ledError, vm.error, ledErrorContainer.height(), ledErrorContainer.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() {
+ if (vm.destroyed) {
+ return;
+ }
+ vm.error = '';
+ vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
+ (responseBody) => {
+ var newValue = responseBody ? true : false;
+ setValue(newValue);
+ if (vm.requestValueTimeoutHandle) {
+ $timeout.cancel(vm.requestValueTimeoutHandle);
+ }
+ vm.requestValueTimeoutHandle = $timeout(rpcRequestValue, vm.valuePollingInterval);
+ },
+ () => {
+ var errorText = vm.ctx.defaultSubscription.rpcErrorText;
+ onError(errorText);
+ if (vm.requestValueTimeoutHandle) {
+ $timeout.cancel(vm.requestValueTimeoutHandle);
+ }
+ vm.requestValueTimeoutHandle = $timeout(rpcRequestValue, vm.valuePollingInterval);
+ }
+ );
+ }
+
+}
ui/src/app/widget/lib/rpc/led-indicator.scss 78(+78 -0)
diff --git a/ui/src/app/widget/lib/rpc/led-indicator.scss b/ui/src/app/widget/lib/rpc/led-indicator.scss
new file mode 100644
index 0000000..d086a10
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/led-indicator.scss
@@ -0,0 +1,78 @@
+/**
+ * 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 "~compass-sass-mixins/lib/compass";
+
+$error-height: 14px;
+
+$background-color: #e6e7e8;
+
+.tb-led-indicator {
+ width:100%;
+ height:100%;
+ background: $background-color;
+
+ .title-container {
+ .led-title {
+ color: #757575;
+ font-weight: 500;
+ white-space: nowrap;
+ }
+ }
+
+ .error-container {
+ position:absolute;
+ top: 1%;
+ left: 0;
+ right: 0;
+ z-index:4;
+ height: $error-height;
+ .led-error {
+ color: #ff3315;
+ white-space: nowrap;
+ }
+ }
+ #text-measure {
+ position: absolute;
+ visibility: hidden;
+ height: auto;
+ width: auto;
+ white-space: nowrap;
+ }
+
+ #led-container {
+ padding: 10px;
+ .led {
+ cursor: pointer;
+ position: relative;
+ border-radius: 50%;
+ background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
+ background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
+ background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
+ background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
+ background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
+ transition: background-color 0.5s, box-shadow 0.5s;
+ &.disabled {
+ background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
+ }
+ }
+ }
+}
+
diff --git a/ui/src/app/widget/lib/rpc/led-indicator.tpl.html b/ui/src/app/widget/lib/rpc/led-indicator.tpl.html
new file mode 100644
index 0000000..a092f42
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/led-indicator.tpl.html
@@ -0,0 +1,32 @@
+<!--
+
+ 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-led-indicator" layout="column">
+ <div flex="20" class="title-container" layout="row" layout-align="center center" ng-show="vm.showTitle">
+ <span class="led-title">{{vm.title}}</span>
+ </div>
+ <div flex="{{vm.showTitle ? 80 : 100}}"
+ ng-style="{paddingTop: vm.showTitle ? '5px': '10px'}" id="led-container" layout="column" layout-align="center center">
+ <div class="led">
+ </div>
+ </div>
+ <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="led-error">{{ vm.error }}</span>
+ </div>
+ <div id="text-measure"></div>
+</div>
ui/src/app/widget/lib/rpc/round-switch.directive.js 198(+198 -0)
diff --git a/ui/src/app/widget/lib/rpc/round-switch.directive.js b/ui/src/app/widget/lib/rpc/round-switch.directive.js
new file mode 100644
index 0000000..edb8c7b
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/round-switch.directive.js
@@ -0,0 +1,198 @@
+/*
+ * 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 './round-switch.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import roundSwitchTemplate from './round-switch.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+export default angular.module('thingsboard.widgets.rpc.roundSwitch', [])
+ .directive('tbRoundSwitch', RoundSwitch)
+ .name;
+
+/*@ngInject*/
+function RoundSwitch() {
+ return {
+ restrict: "E",
+ scope: true,
+ bindToController: {
+ ctx: '='
+ },
+ controller: RoundSwitchController,
+ controllerAs: 'vm',
+ templateUrl: roundSwitchTemplate
+ };
+}
+
+/*@ngInject*/
+function RoundSwitchController($element, $scope, utils) {
+ let vm = this;
+
+ vm.showTitle = false;
+ vm.value = false;
+ vm.error = '';
+
+ vm.checkboxId = 'onoff-' + utils.guid();
+
+ var switchElement = angular.element('.switch', $element),
+ switchContainer = angular.element('#switch-container', $element),
+ onoff = angular.element('input', $element),
+ textMeasure = angular.element('#text-measure', $element),
+ switchTitleContainer = angular.element('.title-container', $element),
+ switchTitle = angular.element('.switch-title', $element),
+ switchErrorContainer = angular.element('.error-container', $element),
+ switchError = angular.element('.switch-error', $element);
+
+ onoff.bind('change', () => {
+ vm.value = onoff.prop('checked') === false;
+ onValue();
+ });
+
+ $scope.$watch('vm.ctx', () => {
+ if (vm.ctx) {
+ init();
+ }
+ });
+
+ resize();
+
+ function init() {
+
+ vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
+ vm.showTitle = vm.title && vm.title.length ? true : false;
+ 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();
+ }
+ }
+ }
+
+ function resize() {
+ var width = switchContainer.width();
+ var height = switchContainer.height();
+ var size = Math.min(width, height);
+ var scale = size/260;
+ switchElement.css({
+ '-webkit-transform': `scale(${scale})`,
+ '-moz-transform': `scale(${scale})`,
+ '-ms-transform': `scale(${scale})`,
+ '-o-transform': `scale(${scale})`,
+ transform: `scale(${scale})`
+ });
+ if (vm.showTitle) {
+ setFontSize(switchTitle, vm.title, switchTitleContainer.height() * 2 / 3, switchTitleContainer.width());
+ }
+ setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
+ }
+
+ function setValue(value) {
+ vm.value = value ? true : false;
+ onoff.prop('checked', !vm.value);
+ }
+
+ 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);
+ }
+ );
+ }
+}
ui/src/app/widget/lib/rpc/round-switch.scss 195(+195 -0)
diff --git a/ui/src/app/widget/lib/rpc/round-switch.scss b/ui/src/app/widget/lib/rpc/round-switch.scss
new file mode 100644
index 0000000..fa2399e
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/round-switch.scss
@@ -0,0 +1,195 @@
+/**
+ * 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 "~compass-sass-mixins/lib/compass";
+
+$error-height: 14px;
+
+$background-color: #e6e7e8;
+
+.tb-round-switch {
+ width:100%;
+ height:100%;
+ background: $background-color;
+
+ .title-container {
+ .switch-title {
+ color: #757575;
+ font-weight: 500;
+ white-space: nowrap;
+ }
+ }
+
+ .error-container {
+ position:absolute;
+ top: 1%;
+ left: 0;
+ right: 0;
+ z-index:4;
+ height: $error-height;
+ .switch-error {
+ color: #ff3315;
+ white-space: nowrap;
+ }
+ }
+ #text-measure {
+ position: absolute;
+ visibility: hidden;
+ height: auto;
+ width: auto;
+ white-space: nowrap;
+ }
+
+ #switch-container {
+ padding: 10px;
+ .switch {
+ cursor: pointer;
+ position: relative;
+ background:#ddd;
+ background: -owg-linear-gradient(270deg, #bbb, #ddd);
+ background: -webkit-linear-gradient(270deg, #bbb, #ddd);
+ background: -moz-linear-gradient(270deg, #bbb, #ddd);
+ background: -o-linear-gradient(270deg, #bbb, #ddd);
+ -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
+ background: linear-gradient(180deg, #bbb, #ddd);
+ border-radius:130px;
+ @include box-sizing(border-box);
+ @include box-shadow(
+ 0px 0px 0px 8px rgba(0,0,0,.1)
+ ,0px 0px 3px 1px rgba(0,0,0,.1)
+ ,inset 0 8px 3px -8px rgba(255,255,255,.4));
+ height: 260px;
+ min-height: 260px;
+ padding: 25px;
+ width: 260px;
+ min-width: 260px;
+
+ color: #424242;
+ font-family:sans-serif;
+ font-size:48px;
+
+ input {
+ display:none
+ }
+
+ .on,.off {
+ position:absolute;
+ text-align:center;
+ @include text-shadow(1px 1px 4px #4a4a4a);
+ width:100%;
+ }
+
+ .on {
+ color:#444;
+ top:10px;
+ @include transition(all 0.1s);
+ font-family:sans-serif
+ }
+
+ .off {
+ bottom:5px;
+ @include transition(all 0.1s);
+ @include transform(scaleY(0.85));
+ }
+
+ .but {
+ cursor: pointer;
+ background-color:#d8d8d8;
+ border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
+ border-bottom-width:0px;
+ @include box-shadow(inset 8px 6px 5px -7px #a2a2a2,
+ inset -8px 6px 5px -7px #a2a2a2,
+ inset 0 -3px 2px -2px rgba(200, 200, 200, 0.5),
+ 0 3px 3px -2px #ffffff,
+ inset 0 -230px 60px -200px rgba(255, 255, 255, 0.2),
+ inset 0 220px 40px -200px rgba(0, 0, 0, 0.3));
+ display:block;
+ font-size:48px;
+ height:178px;
+ position:relative;
+ @include transition(all 0.2s);
+ width:200px;
+ }
+
+ .back {
+ cursor: pointer;
+ background-color: #888787;
+ background-image: -owg-linear-gradient(0deg, transparent 30%, transparent 70%), -owg-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
+ background-image: -webkit-linear-gradient(0deg, transparent 30%, transparent 70%), -webkit-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
+ background-image: -moz-linear-gradient(0deg, transparent 30%, transparent 70%), -moz-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
+ background-image: -o-linear-gradient(0deg, transparent 30%, transparent 70%), -o-linear-gradient(90deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
+ background-image: linear-gradient(-90deg, transparent 30%, transparent 70%), linear-gradient(0deg, rgba(150, 150, 150, 0) 30%, rgba(150, 150, 150, 0.2) 50%, rgba(150, 150, 150, 0) 70%);
+ border-radius:105px;
+ @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, 0.3),
+ -30px 30px 30px -20px rgba(58, 58, 58, 0.3),
+ 0 30px 30px 0px rgba(16, 16, 16, 0.3),
+ inset 0 -1px 0 0 #484848);
+ @include box-sizing(border-box);
+ height:210px;
+ padding:4px 4px;
+ @include transition(all 0.2s);
+ width:210px;
+ }
+
+
+ input:checked + .back .on,input:checked + .back .off{
+ @include text-shadow(1px 1px 4px #4a4a4a);
+ }
+ input:checked + .back .on{
+ color:#4c4c4c;
+ top:10px;
+ @include transform(scaleY(0.85));
+ }
+ input:checked + .back .off{
+ color:#444;
+ bottom:5px;
+ @include transform(scaleY(1));
+ }
+ input:checked + .back .but{
+ background:#dcdcdc;
+ background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
+ border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
+ @include box-shadow(inset 8px -4px 5px -7px #a9a9a9,
+ inset -8px -4px 5px -7px #808080,
+ 0 -3px 8px -4px rgba(50, 50, 50, 0.4),
+ inset 0 3px 4px -2px #9c9c9c,
+ inset 0 280px 40px -200px rgba(0, 0, 0, 0.2),
+ inset 0 -200px 40px -200px rgba(180, 180, 180, 0.2));
+ margin-top:20px;
+ }
+ input:checked + .back{
+
+ background-image: -owg-linear-gradient(90deg, #868686 30%, transparent 70%), -owg-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
+ background-image: -webkit-linear-gradient(90deg, #868686 30%, transparent 70%), -webkit-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
+ background-image: -moz-linear-gradient(90deg, #868686 30%, transparent 70%), -moz-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
+ background-image: -o-linear-gradient(90deg, #868686 30%, transparent 70%), -o-linear-gradient(180deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
+ background-image: linear-gradient(0deg, #868686 30%, transparent 70%), linear-gradient(90deg, rgba(115, 115, 115, 0) 0%, rgba(255, 255, 255, 0.74) 50%, rgba(105, 105, 105, 0) 100%);
+
+ @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, 0.1),
+ -30px 30px 30px -20px rgba(111, 111, 111, 0.1),
+ 0 30px 30px 0px rgba(0, 0, 0, 0.2),
+ inset 0 1px 2px 0 rgba(167, 167, 167, 0.6));
+ padding:2px 4px;
+ }
+
+ }
+ }
+}
+
diff --git a/ui/src/app/widget/lib/rpc/round-switch.tpl.html b/ui/src/app/widget/lib/rpc/round-switch.tpl.html
new file mode 100644
index 0000000..ad8c1a6
--- /dev/null
+++ b/ui/src/app/widget/lib/rpc/round-switch.tpl.html
@@ -0,0 +1,38 @@
+<!--
+
+ 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-round-switch" layout="column" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
+ <div flex="20" 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 ? 80 : 100}}" ng-style="{paddingTop: vm.showTitle ? '5px': '10px'}" id="switch-container" layout="column" layout-align="center center">
+ <div class="switch">
+ <input type="checkbox" id="{{vm.checkboxId}}" name="onoff" />
+ <div class="back">
+ <label class="but" for="{{vm.checkboxId}}">
+ <span class="on">I</span>
+ <span class="off">0</span>
+ </label>
+ </div>
+ </div>
+ </div>
+ <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 id="text-measure"></div>
+</div>