Commit ce48c4ac6a7666e5956fb44b0f5a3db0341f20d9

Authored by Igor Kulikov
1 parent e9730fec

TB-70: RPC widgets: Add round switch control widget.

... ... @@ -68,6 +68,22 @@
68 68 "dataKeySettingsSchema": "{}\n",
69 69 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
70 70 }
  71 + },
  72 + {
  73 + "alias": "round_switch",
  74 + "name": "Round switch",
  75 + "descriptor": {
  76 + "type": "rpc",
  77 + "sizeX": 2.5,
  78 + "sizeY": 2,
  79 + "resources": [],
  80 + "templateHtml": "<tb-round-switch ctx='ctx'></tb-round-switch>",
  81 + "templateCss": "",
  82 + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
  83 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}",
  84 + "dataKeySettingsSchema": "{}\n",
  85 + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Round switch\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
  86 + }
71 87 }
72 88 ]
73 89 }
\ No newline at end of file
... ...
... ... @@ -16,8 +16,10 @@
16 16
17 17 import tbKnob from './knob.directive';
18 18 import tbSwitch from './switch.directive';
  19 +import tbRoundSwitch from './round-switch.directive';
19 20
20 21 export default angular.module('thingsboard.widgets.rpc', [
21 22 tbKnob,
22   - tbSwitch
  23 + tbSwitch,
  24 + tbRoundSwitch
23 25 ]).name;
... ...
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import './round-switch.scss';
  18 +
  19 +/* eslint-disable import/no-unresolved, import/default */
  20 +
  21 +import roundSwitchTemplate from './round-switch.tpl.html';
  22 +
  23 +/* eslint-enable import/no-unresolved, import/default */
  24 +
  25 +export default angular.module('thingsboard.widgets.rpc.roundSwitch', [])
  26 + .directive('tbRoundSwitch', RoundSwitch)
  27 + .name;
  28 +
  29 +/*@ngInject*/
  30 +function RoundSwitch() {
  31 + return {
  32 + restrict: "E",
  33 + scope: true,
  34 + bindToController: {
  35 + ctx: '='
  36 + },
  37 + controller: RoundSwitchController,
  38 + controllerAs: 'vm',
  39 + templateUrl: roundSwitchTemplate
  40 + };
  41 +}
  42 +
  43 +/*@ngInject*/
  44 +function RoundSwitchController($element, $scope, utils) {
  45 + let vm = this;
  46 +
  47 + vm.showTitle = false;
  48 + vm.value = false;
  49 + vm.error = '';
  50 +
  51 + vm.checkboxId = 'onoff-' + utils.guid();
  52 +
  53 + var switchElement = angular.element('.switch', $element),
  54 + switchContainer = angular.element('#switch-container', $element),
  55 + onoff = angular.element('input', $element),
  56 + textMeasure = angular.element('#text-measure', $element),
  57 + switchTitleContainer = angular.element('.title-container', $element),
  58 + switchTitle = angular.element('.switch-title', $element),
  59 + switchErrorContainer = angular.element('.error-container', $element),
  60 + switchError = angular.element('.switch-error', $element);
  61 +
  62 + onoff.bind('change', () => {
  63 + vm.value = onoff.prop('checked') === false;
  64 + onValue();
  65 + });
  66 +
  67 + $scope.$watch('vm.ctx', () => {
  68 + if (vm.ctx) {
  69 + init();
  70 + }
  71 + });
  72 +
  73 + resize();
  74 +
  75 + function init() {
  76 +
  77 + vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
  78 + vm.showTitle = vm.title && vm.title.length ? true : false;
  79 + vm.showOnOffLabels = angular.isDefined(vm.ctx.settings.showOnOffLabels) ? vm.ctx.settings.showOnOffLabels : true;
  80 + vm.ctx.resize = resize;
  81 + $scope.$applyAsync(() => {
  82 + resize();
  83 + });
  84 + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : false;
  85 + setValue(initialValue);
  86 +
  87 + var subscription = vm.ctx.defaultSubscription;
  88 + var rpcEnabled = subscription.rpcEnabled;
  89 +
  90 + vm.isSimulated = $scope.widgetEditMode;
  91 +
  92 + vm.requestTimeout = 500;
  93 + if (vm.ctx.settings.requestTimeout) {
  94 + vm.requestTimeout = vm.ctx.settings.requestTimeout;
  95 + }
  96 + vm.getValueMethod = 'getValue';
  97 + if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
  98 + vm.getValueMethod = vm.ctx.settings.getValueMethod;
  99 + }
  100 + vm.setValueMethod = 'setValue';
  101 + if (vm.ctx.settings.setValueMethod && vm.ctx.settings.setValueMethod.length) {
  102 + vm.setValueMethod = vm.ctx.settings.setValueMethod;
  103 + }
  104 + if (!rpcEnabled) {
  105 + onError('Target device is not set!');
  106 + } else {
  107 + if (!vm.isSimulated) {
  108 + rpcRequestValue();
  109 + }
  110 + }
  111 + }
  112 +
  113 + function resize() {
  114 + var width = switchContainer.width();
  115 + var height = switchContainer.height();
  116 + var size = Math.min(width, height);
  117 + var scale = size/260;
  118 + switchElement.css({
  119 + '-webkit-transform': `scale(${scale})`,
  120 + '-moz-transform': `scale(${scale})`,
  121 + '-ms-transform': `scale(${scale})`,
  122 + '-o-transform': `scale(${scale})`,
  123 + transform: `scale(${scale})`
  124 + });
  125 + if (vm.showTitle) {
  126 + setFontSize(switchTitle, vm.title, switchTitleContainer.height() * 2 / 3, switchTitleContainer.width());
  127 + }
  128 + setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
  129 + }
  130 +
  131 + function setValue(value) {
  132 + vm.value = value ? true : false;
  133 + onoff.prop('checked', !vm.value);
  134 + }
  135 +
  136 + function onValue() {
  137 + rpcUpdateValue(vm.value);
  138 + }
  139 +
  140 + function onError(error) {
  141 + $scope.$applyAsync(() => {
  142 + vm.error = error;
  143 + setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width());
  144 + });
  145 + }
  146 +
  147 + function setFontSize(element, text, fontSize, maxWidth) {
  148 + var textWidth = measureTextWidth(text, fontSize);
  149 + while (textWidth > maxWidth) {
  150 + fontSize--;
  151 + textWidth = measureTextWidth(text, fontSize);
  152 + }
  153 + element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  154 + }
  155 +
  156 + function measureTextWidth(text, fontSize) {
  157 + textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  158 + textMeasure.text(text);
  159 + return textMeasure.width();
  160 + }
  161 +
  162 + function rpcRequestValue() {
  163 + vm.error = '';
  164 + vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
  165 + (responseBody) => {
  166 + setValue(responseBody);
  167 + },
  168 + () => {
  169 + var errorText = vm.ctx.defaultSubscription.rpcErrorText;
  170 + onError(errorText);
  171 + }
  172 + );
  173 + }
  174 +
  175 + function rpcUpdateValue(value) {
  176 + if (vm.executingUpdateValue) {
  177 + vm.scheduledValue = value;
  178 + return;
  179 + } else {
  180 + vm.scheduledValue = null;
  181 + vm.rpcValue = value;
  182 + vm.executingUpdateValue = true;
  183 + }
  184 + vm.error = '';
  185 + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then(
  186 + () => {
  187 + vm.executingUpdateValue = false;
  188 + if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) {
  189 + rpcUpdateValue(vm.scheduledValue);
  190 + }
  191 + },
  192 + () => {
  193 + vm.executingUpdateValue = false;
  194 + var errorText = vm.ctx.defaultSubscription.rpcErrorText;
  195 + onError(errorText);
  196 + }
  197 + );
  198 + }
  199 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +@import "~compass-sass-mixins/lib/compass";
  18 +
  19 +$error-height: 14px;
  20 +
  21 +$background-color: #e6e7e8;
  22 +
  23 +.tb-round-switch {
  24 + width:100%;
  25 + height:100%;
  26 + background: $background-color;
  27 +
  28 + .title-container {
  29 + .switch-title {
  30 + color: #757575;
  31 + font-weight: 500;
  32 + white-space: nowrap;
  33 + }
  34 + }
  35 +
  36 + .error-container {
  37 + position:absolute;
  38 + top: 1%;
  39 + left: 0;
  40 + right: 0;
  41 + z-index:4;
  42 + height: $error-height;
  43 + .switch-error {
  44 + color: #ff3315;
  45 + white-space: nowrap;
  46 + }
  47 + }
  48 + #text-measure {
  49 + position: absolute;
  50 + visibility: hidden;
  51 + height: auto;
  52 + width: auto;
  53 + white-space: nowrap;
  54 + }
  55 +
  56 + #switch-container {
  57 + padding: 10px;
  58 + .switch {
  59 + cursor: pointer;
  60 + position: relative;
  61 + background:#ddd;
  62 + background: -owg-linear-gradient(270deg, #bbb, #ddd);
  63 + background: -webkit-linear-gradient(270deg, #bbb, #ddd);
  64 + background: -moz-linear-gradient(270deg, #bbb, #ddd);
  65 + background: -o-linear-gradient(270deg, #bbb, #ddd);
  66 + -pie-background: -pie-linear-gradient(270deg, #bbb, #ddd);
  67 + background: linear-gradient(180deg, #bbb, #ddd);
  68 + border-radius:130px;
  69 + @include box-sizing(border-box);
  70 + @include box-shadow(
  71 + 0px 0px 0px 8px rgba(0,0,0,.1)
  72 + ,0px 0px 3px 1px rgba(0,0,0,.1)
  73 + ,inset 0 8px 3px -8px rgba(255,255,255,.4));
  74 + height: 260px;
  75 + min-height: 260px;
  76 + padding: 25px;
  77 + width: 260px;
  78 + min-width: 260px;
  79 +
  80 + color: #424242;
  81 + font-family:sans-serif;
  82 + font-size:48px;
  83 +
  84 + input {
  85 + display:none
  86 + }
  87 +
  88 + .on,.off {
  89 + position:absolute;
  90 + text-align:center;
  91 + @include text-shadow(1px 1px 4px #4a4a4a);
  92 + width:100%;
  93 + }
  94 +
  95 + .on {
  96 + color:#444;
  97 + top:10px;
  98 + @include transition(all 0.1s);
  99 + font-family:sans-serif
  100 + }
  101 +
  102 + .off {
  103 + bottom:5px;
  104 + @include transition(all 0.1s);
  105 + @include transform(scaleY(0.85));
  106 + }
  107 +
  108 + .but {
  109 + cursor: pointer;
  110 + background-color:#d8d8d8;
  111 + border-radius: 400px 400px 400px 400px / 400px 400px 300px 300px;
  112 + border-bottom-width:0px;
  113 + @include box-shadow(inset 8px 6px 5px -7px #a2a2a2,
  114 + inset -8px 6px 5px -7px #a2a2a2,
  115 + inset 0 -3px 2px -2px rgba(200, 200, 200, 0.5),
  116 + 0 3px 3px -2px #ffffff,
  117 + inset 0 -230px 60px -200px rgba(255, 255, 255, 0.2),
  118 + inset 0 220px 40px -200px rgba(0, 0, 0, 0.3));
  119 + display:block;
  120 + font-size:48px;
  121 + height:178px;
  122 + position:relative;
  123 + @include transition(all 0.2s);
  124 + width:200px;
  125 + }
  126 +
  127 + .back {
  128 + cursor: pointer;
  129 + background-color: #888787;
  130 + 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%);
  131 + 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%);
  132 + 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%);
  133 + 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%);
  134 + 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%);
  135 + border-radius:105px;
  136 + @include box-shadow(30px 30px 30px -20px rgba(58, 58, 58, 0.3),
  137 + -30px 30px 30px -20px rgba(58, 58, 58, 0.3),
  138 + 0 30px 30px 0px rgba(16, 16, 16, 0.3),
  139 + inset 0 -1px 0 0 #484848);
  140 + @include box-sizing(border-box);
  141 + height:210px;
  142 + padding:4px 4px;
  143 + @include transition(all 0.2s);
  144 + width:210px;
  145 + }
  146 +
  147 +
  148 + input:checked + .back .on,input:checked + .back .off{
  149 + @include text-shadow(1px 1px 4px #4a4a4a);
  150 + }
  151 + input:checked + .back .on{
  152 + color:#4c4c4c;
  153 + top:10px;
  154 + @include transform(scaleY(0.85));
  155 + }
  156 + input:checked + .back .off{
  157 + color:#444;
  158 + bottom:5px;
  159 + @include transform(scaleY(1));
  160 + }
  161 + input:checked + .back .but{
  162 + background:#dcdcdc;
  163 + background-image: -owg-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  164 + background-image: -webkit-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  165 + background-image: -moz-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  166 + background-image: -o-radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  167 + background-image: radial-gradient(50% 15%, circle closest-corner, rgba(0, 0, 0, 0.3), transparent);
  168 + border-radius: 400px 400px 400px 400px / 300px 300px 400px 400px;
  169 + @include box-shadow(inset 8px -4px 5px -7px #a9a9a9,
  170 + inset -8px -4px 5px -7px #808080,
  171 + 0 -3px 8px -4px rgba(50, 50, 50, 0.4),
  172 + inset 0 3px 4px -2px #9c9c9c,
  173 + inset 0 280px 40px -200px rgba(0, 0, 0, 0.2),
  174 + inset 0 -200px 40px -200px rgba(180, 180, 180, 0.2));
  175 + margin-top:20px;
  176 + }
  177 + input:checked + .back{
  178 +
  179 + 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%);
  180 + 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%);
  181 + 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%);
  182 + 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%);
  183 + 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%);
  184 +
  185 + @include box-shadow(30px 30px 30px -20px rgba(49, 49, 49, 0.1),
  186 + -30px 30px 30px -20px rgba(111, 111, 111, 0.1),
  187 + 0 30px 30px 0px rgba(0, 0, 0, 0.2),
  188 + inset 0 1px 2px 0 rgba(167, 167, 167, 0.6));
  189 + padding:2px 4px;
  190 + }
  191 +
  192 + }
  193 + }
  194 +}
  195 +
... ...
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<div class="tb-round-switch" layout="column" ng-style="{'pointerEvents': vm.ctx.isEdit ? 'none' : 'all'}">
  19 + <div flex="20" class="title-container" layout="row" layout-align="center center" ng-show="vm.showTitle">
  20 + <span class="switch-title">{{vm.title}}</span>
  21 + </div>
  22 + <div flex="{{vm.showTitle ? 80 : 100}}" ng-style="{paddingTop: vm.showTitle ? '5px': '10px'}" id="switch-container" layout="column" layout-align="center center">
  23 + <div class="switch">
  24 + <input type="checkbox" id="{{vm.checkboxId}}" name="onoff" />
  25 + <div class="back">
  26 + <label class="but" for="{{vm.checkboxId}}">
  27 + <span class="on">I</span>
  28 + <span class="off">0</span>
  29 + </label>
  30 + </div>
  31 + </div>
  32 + </div>
  33 + <div class="error-container" ng-style="{'background': vm.error.length ? 'rgba(255,255,255,0.25)' : 'none'}"
  34 + layout="row" layout-align="center center">
  35 + <span class="switch-error">{{ vm.error }}</span>
  36 + </div>
  37 + <div id="text-measure"></div>
  38 +</div>
... ...