Commit b7c50950ad5612178e66a5ee2c8169fb9140fb38
1 parent
6357544c
TB-70: RPC Widgets: LED indicator.
Showing
6 changed files
with
336 additions
and
2 deletions
@@ -84,6 +84,22 @@ | @@ -84,6 +84,22 @@ | ||
84 | "dataKeySettingsSchema": "{}\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}" | 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 | } |
@@ -17,9 +17,11 @@ | @@ -17,9 +17,11 @@ | ||
17 | import tbKnob from './knob.directive'; | 17 | import tbKnob from './knob.directive'; |
18 | import tbSwitch from './switch.directive'; | 18 | import tbSwitch from './switch.directive'; |
19 | import tbRoundSwitch from './round-switch.directive'; | 19 | import tbRoundSwitch from './round-switch.directive'; |
20 | +import tbLedIndicator from './led-indicator.directive'; | ||
20 | 21 | ||
21 | export default angular.module('thingsboard.widgets.rpc', [ | 22 | export default angular.module('thingsboard.widgets.rpc', [ |
22 | tbKnob, | 23 | tbKnob, |
23 | tbSwitch, | 24 | tbSwitch, |
24 | - tbRoundSwitch | 25 | + tbRoundSwitch, |
26 | + tbLedIndicator | ||
25 | ]).name; | 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 | +} |
ui/src/app/widget/lib/rpc/led-indicator.scss
0 → 100644
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,7 +76,6 @@ function RoundSwitchController($element, $scope, utils) { | ||
76 | 76 | ||
77 | vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : ''; | 77 | vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : ''; |
78 | vm.showTitle = vm.title && vm.title.length ? true : false; | 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; | 79 | vm.ctx.resize = resize; |
81 | $scope.$applyAsync(() => { | 80 | $scope.$applyAsync(() => { |
82 | resize(); | 81 | resize(); |