Commit b7c50950ad5612178e66a5ee2c8169fb9140fb38

Authored by Igor Kulikov
1 parent 6357544c

TB-70: RPC Widgets: LED indicator.

... ... @@ -84,6 +84,22 @@
84 84 "dataKeySettingsSchema": "{}\n",
85 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 86 }
  87 + },
  88 + {
  89 + "alias": "led_indicator",
  90 + "name": "Led indicator",
  91 + "descriptor": {
  92 + "type": "rpc",
  93 + "sizeX": 2.5,
  94 + "sizeY": 2.5,
  95 + "resources": [],
  96 + "templateHtml": "<tb-led-indicator ctx='ctx'></tb-led-indicator>",
  97 + "templateCss": "",
  98 + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n",
  99 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"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}",
  100 + "dataKeySettingsSchema": "{}\n",
  101 + "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}"
  102 + }
87 103 }
88 104 ]
89 105 }
\ No newline at end of file
... ...
... ... @@ -17,9 +17,11 @@
17 17 import tbKnob from './knob.directive';
18 18 import tbSwitch from './switch.directive';
19 19 import tbRoundSwitch from './round-switch.directive';
  20 +import tbLedIndicator from './led-indicator.directive';
20 21
21 22 export default angular.module('thingsboard.widgets.rpc', [
22 23 tbKnob,
23 24 tbSwitch,
24   - tbRoundSwitch
  25 + tbRoundSwitch,
  26 + tbLedIndicator
25 27 ]).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 './led-indicator.scss';
  18 +
  19 +import tinycolor from 'tinycolor2';
  20 +
  21 +/* eslint-disable import/no-unresolved, import/default */
  22 +
  23 +import ledIndicatorTemplate from './led-indicator.tpl.html';
  24 +
  25 +/* eslint-enable import/no-unresolved, import/default */
  26 +
  27 +export default angular.module('thingsboard.widgets.rpc.ledIndicator', [])
  28 + .directive('tbLedIndicator', LedIndicator)
  29 + .name;
  30 +
  31 +/*@ngInject*/
  32 +function LedIndicator() {
  33 + return {
  34 + restrict: "E",
  35 + scope: true,
  36 + bindToController: {
  37 + ctx: '='
  38 + },
  39 + controller: LedIndicatorController,
  40 + controllerAs: 'vm',
  41 + templateUrl: ledIndicatorTemplate
  42 + };
  43 +}
  44 +
  45 +/*@ngInject*/
  46 +function LedIndicatorController($element, $scope, $timeout) {
  47 + let vm = this;
  48 +
  49 + vm.showTitle = false;
  50 + vm.value = false;
  51 + vm.error = '';
  52 +
  53 + var led = angular.element('.led', $element),
  54 + ledContainer = angular.element('#led-container', $element),
  55 + textMeasure = angular.element('#text-measure', $element),
  56 + ledTitleContainer = angular.element('.title-container', $element),
  57 + ledTitle = angular.element('.led-title', $element),
  58 + ledErrorContainer = angular.element('.error-container', $element),
  59 + ledError = angular.element('.led-error', $element);
  60 +
  61 + $scope.$watch('vm.ctx', () => {
  62 + if (vm.ctx) {
  63 + init();
  64 + }
  65 + });
  66 +
  67 + $scope.$on('$destroy', () => {
  68 + vm.destroyed = true;
  69 + if (vm.requestValueTimeoutHandle) {
  70 + $timeout.cancel(vm.requestValueTimeoutHandle);
  71 + }
  72 + });
  73 +
  74 + resize();
  75 +
  76 + function init() {
  77 +
  78 + vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
  79 + vm.showTitle = vm.title && vm.title.length ? true : false;
  80 +
  81 + var origColor = angular.isDefined(vm.ctx.settings.ledColor) ? vm.ctx.settings.ledColor : 'green';
  82 +
  83 + vm.ledColor = tinycolor(origColor).brighten(30).toHexString();
  84 + vm.ledMiddleColor = tinycolor(origColor).toHexString();
  85 + vm.disabledColor = tinycolor(origColor).darken(40).toHexString();
  86 + vm.disabledMiddleColor = tinycolor(origColor).darken(60).toHexString();
  87 +
  88 + vm.ctx.resize = resize;
  89 + $scope.$applyAsync(() => {
  90 + resize();
  91 + });
  92 + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : false;
  93 + setValue(initialValue, true);
  94 +
  95 + var subscription = vm.ctx.defaultSubscription;
  96 + var rpcEnabled = subscription.rpcEnabled;
  97 +
  98 + vm.isSimulated = $scope.widgetEditMode;
  99 +
  100 + vm.requestTimeout = 500;
  101 + if (vm.ctx.settings.requestTimeout) {
  102 + vm.requestTimeout = vm.ctx.settings.requestTimeout;
  103 + }
  104 + vm.valuePollingInterval = 500;
  105 + if (vm.ctx.settings.valuePollingInterval) {
  106 + vm.valuePollingInterval = vm.ctx.settings.valuePollingInterval;
  107 + }
  108 + vm.getValueMethod = 'getValue';
  109 + if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) {
  110 + vm.getValueMethod = vm.ctx.settings.getValueMethod;
  111 + }
  112 + if (!rpcEnabled) {
  113 + onError('Target device is not set!');
  114 + } else {
  115 + if (!vm.isSimulated) {
  116 + rpcRequestValue();
  117 + }
  118 + }
  119 + }
  120 +
  121 + function resize() {
  122 + var width = ledContainer.width();
  123 + var height = ledContainer.height();
  124 + var size = Math.min(width, height);
  125 +
  126 + led.css({width: size, height: size});
  127 +
  128 + if (vm.showTitle) {
  129 + setFontSize(ledTitle, vm.title, ledTitleContainer.height() * 2 / 3, ledTitleContainer.width());
  130 + }
  131 + setFontSize(ledError, vm.error, ledErrorContainer.height(), ledErrorContainer.width());
  132 + }
  133 +
  134 + function setValue(value, forceUpdate) {
  135 + if (vm.value != value || forceUpdate) {
  136 + vm.value = value;
  137 + updateColor();
  138 + }
  139 + }
  140 +
  141 + function updateColor() {
  142 + var color = vm.value ? vm.ledColor : vm.disabledColor;
  143 + var middleColor = vm.value ? vm.ledMiddleColor : vm.disabledMiddleColor;
  144 + var boxShadow = `#000 0 -1px 6px 1px, inset ${middleColor} 0 -1px 8px, ${color} 0 3px 11px`;
  145 + led.css({'backgroundColor': color});
  146 + led.css({'boxShadow': boxShadow});
  147 + if (vm.value) {
  148 + led.removeClass( 'disabled' );
  149 + } else {
  150 + led.addClass( 'disabled' );
  151 + }
  152 + }
  153 +
  154 + function onError(error) {
  155 + $scope.$applyAsync(() => {
  156 + vm.error = error;
  157 + setFontSize(ledError, vm.error, ledErrorContainer.height(), ledErrorContainer.width());
  158 + });
  159 + }
  160 +
  161 + function setFontSize(element, text, fontSize, maxWidth) {
  162 + var textWidth = measureTextWidth(text, fontSize);
  163 + while (textWidth > maxWidth) {
  164 + fontSize--;
  165 + textWidth = measureTextWidth(text, fontSize);
  166 + }
  167 + element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  168 + }
  169 +
  170 + function measureTextWidth(text, fontSize) {
  171 + textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'});
  172 + textMeasure.text(text);
  173 + return textMeasure.width();
  174 + }
  175 +
  176 + function rpcRequestValue() {
  177 + if (vm.destroyed) {
  178 + return;
  179 + }
  180 + vm.error = '';
  181 + vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then(
  182 + (responseBody) => {
  183 + var newValue = responseBody ? true : false;
  184 + setValue(newValue);
  185 + if (vm.requestValueTimeoutHandle) {
  186 + $timeout.cancel(vm.requestValueTimeoutHandle);
  187 + }
  188 + vm.requestValueTimeoutHandle = $timeout(rpcRequestValue, vm.valuePollingInterval);
  189 + },
  190 + () => {
  191 + var errorText = vm.ctx.defaultSubscription.rpcErrorText;
  192 + onError(errorText);
  193 + if (vm.requestValueTimeoutHandle) {
  194 + $timeout.cancel(vm.requestValueTimeoutHandle);
  195 + }
  196 + vm.requestValueTimeoutHandle = $timeout(rpcRequestValue, vm.valuePollingInterval);
  197 + }
  198 + );
  199 + }
  200 +
  201 +}
... ...
  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-led-indicator {
  24 + width:100%;
  25 + height:100%;
  26 + background: $background-color;
  27 +
  28 + .title-container {
  29 + .led-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 + .led-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 + #led-container {
  57 + padding: 10px;
  58 + .led {
  59 + cursor: pointer;
  60 + position: relative;
  61 + border-radius: 50%;
  62 + //background-color: rgba(0, 0, 9, 0.5);
  63 + //box-shadow: #000 0 -1px 6px 1px;
  64 + //background-color: #FF0;
  65 + //box-shadow: #000 0 -1px 6px 1px, inset #660 0 -1px 8px, #FF0 0 3px 11px;
  66 + //rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #808002 0 -1px 9px, #FF0 0 2px 12px;
  67 + background-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
  68 + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
  69 + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
  70 + background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
  71 + background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25));
  72 + transition: background-color 0.5s, box-shadow 0.5s;//, background-image 0.5s;
  73 + &.disabled {
  74 + background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  75 + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  76 + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  77 + background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  78 + background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1));
  79 + //transition: background-color 0.5s, box-shadow 0.5s, background-image 0.5s;
  80 + }
  81 + }
  82 + }
  83 +}
  84 +
... ...
  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-led-indicator" layout="column">
  19 + <div flex="20" class="title-container" layout="row" layout-align="center center" ng-show="vm.showTitle">
  20 + <span class="led-title">{{vm.title}}</span>
  21 + </div>
  22 + <div flex="{{vm.showTitle ? 80 : 100}}"
  23 + ng-style="{paddingTop: vm.showTitle ? '5px': '10px'}" id="led-container" layout="column" layout-align="center center">
  24 + <div class="led">
  25 + </div>
  26 + </div>
  27 + <div class="error-container" ng-style="{'background': vm.error.length ? 'rgba(255,255,255,0.25)' : 'none'}"
  28 + layout="row" layout-align="center center">
  29 + <span class="led-error">{{ vm.error }}</span>
  30 + </div>
  31 + <div id="text-measure"></div>
  32 +</div>
... ...
... ... @@ -76,7 +76,6 @@ function RoundSwitchController($element, $scope, utils) {
76 76
77 77 vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
78 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 79 vm.ctx.resize = resize;
81 80 $scope.$applyAsync(() => {
82 81 resize();
... ...