Showing
8 changed files
with
779 additions
and
1 deletions
... | ... | @@ -68,6 +68,38 @@ |
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 | + } | |
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 | + } | |
71 | 103 | } |
72 | 104 | ] |
73 | 105 | } |
\ No newline at end of file | ... | ... |
... | ... | @@ -16,8 +16,12 @@ |
16 | 16 | |
17 | 17 | import tbKnob from './knob.directive'; |
18 | 18 | import tbSwitch from './switch.directive'; |
19 | +import tbRoundSwitch from './round-switch.directive'; | |
20 | +import tbLedIndicator from './led-indicator.directive'; | |
19 | 21 | |
20 | 22 | export default angular.module('thingsboard.widgets.rpc', [ |
21 | 23 | tbKnob, |
22 | - tbSwitch | |
24 | + tbSwitch, | |
25 | + tbRoundSwitch, | |
26 | + tbLedIndicator | |
23 | 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-image: -owg-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25)); | |
63 | + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25)); | |
64 | + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25)); | |
65 | + background-image: -o-radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25)); | |
66 | + background-image: radial-gradient(50% 50%, circle closest-corner, transparent, rgba(0, 0, 0, 0.25)); | |
67 | + transition: background-color 0.5s, box-shadow 0.5s; | |
68 | + &.disabled { | |
69 | + background-image: -owg-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1)); | |
70 | + background-image: -webkit-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1)); | |
71 | + background-image: -moz-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1)); | |
72 | + background-image: -o-radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1)); | |
73 | + background-image: radial-gradient(50% 50%, circle closest-corner, rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.1)); | |
74 | + } | |
75 | + } | |
76 | + } | |
77 | +} | |
78 | + | ... | ... |
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> | ... | ... |
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.ctx.resize = resize; | |
80 | + $scope.$applyAsync(() => { | |
81 | + resize(); | |
82 | + }); | |
83 | + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : false; | |
84 | + setValue(initialValue); | |
85 | + | |
86 | + var subscription = vm.ctx.defaultSubscription; | |
87 | + var rpcEnabled = subscription.rpcEnabled; | |
88 | + | |
89 | + vm.isSimulated = $scope.widgetEditMode; | |
90 | + | |
91 | + vm.requestTimeout = 500; | |
92 | + if (vm.ctx.settings.requestTimeout) { | |
93 | + vm.requestTimeout = vm.ctx.settings.requestTimeout; | |
94 | + } | |
95 | + vm.getValueMethod = 'getValue'; | |
96 | + if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { | |
97 | + vm.getValueMethod = vm.ctx.settings.getValueMethod; | |
98 | + } | |
99 | + vm.setValueMethod = 'setValue'; | |
100 | + if (vm.ctx.settings.setValueMethod && vm.ctx.settings.setValueMethod.length) { | |
101 | + vm.setValueMethod = vm.ctx.settings.setValueMethod; | |
102 | + } | |
103 | + if (!rpcEnabled) { | |
104 | + onError('Target device is not set!'); | |
105 | + } else { | |
106 | + if (!vm.isSimulated) { | |
107 | + rpcRequestValue(); | |
108 | + } | |
109 | + } | |
110 | + } | |
111 | + | |
112 | + function resize() { | |
113 | + var width = switchContainer.width(); | |
114 | + var height = switchContainer.height(); | |
115 | + var size = Math.min(width, height); | |
116 | + var scale = size/260; | |
117 | + switchElement.css({ | |
118 | + '-webkit-transform': `scale(${scale})`, | |
119 | + '-moz-transform': `scale(${scale})`, | |
120 | + '-ms-transform': `scale(${scale})`, | |
121 | + '-o-transform': `scale(${scale})`, | |
122 | + transform: `scale(${scale})` | |
123 | + }); | |
124 | + if (vm.showTitle) { | |
125 | + setFontSize(switchTitle, vm.title, switchTitleContainer.height() * 2 / 3, switchTitleContainer.width()); | |
126 | + } | |
127 | + setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); | |
128 | + } | |
129 | + | |
130 | + function setValue(value) { | |
131 | + vm.value = value ? true : false; | |
132 | + onoff.prop('checked', !vm.value); | |
133 | + } | |
134 | + | |
135 | + function onValue() { | |
136 | + rpcUpdateValue(vm.value); | |
137 | + } | |
138 | + | |
139 | + function onError(error) { | |
140 | + $scope.$applyAsync(() => { | |
141 | + vm.error = error; | |
142 | + setFontSize(switchError, vm.error, switchErrorContainer.height(), switchErrorContainer.width()); | |
143 | + }); | |
144 | + } | |
145 | + | |
146 | + function setFontSize(element, text, fontSize, maxWidth) { | |
147 | + var textWidth = measureTextWidth(text, fontSize); | |
148 | + while (textWidth > maxWidth) { | |
149 | + fontSize--; | |
150 | + textWidth = measureTextWidth(text, fontSize); | |
151 | + } | |
152 | + element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'}); | |
153 | + } | |
154 | + | |
155 | + function measureTextWidth(text, fontSize) { | |
156 | + textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'}); | |
157 | + textMeasure.text(text); | |
158 | + return textMeasure.width(); | |
159 | + } | |
160 | + | |
161 | + function rpcRequestValue() { | |
162 | + vm.error = ''; | |
163 | + vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( | |
164 | + (responseBody) => { | |
165 | + setValue(responseBody); | |
166 | + }, | |
167 | + () => { | |
168 | + var errorText = vm.ctx.defaultSubscription.rpcErrorText; | |
169 | + onError(errorText); | |
170 | + } | |
171 | + ); | |
172 | + } | |
173 | + | |
174 | + function rpcUpdateValue(value) { | |
175 | + if (vm.executingUpdateValue) { | |
176 | + vm.scheduledValue = value; | |
177 | + return; | |
178 | + } else { | |
179 | + vm.scheduledValue = null; | |
180 | + vm.rpcValue = value; | |
181 | + vm.executingUpdateValue = true; | |
182 | + } | |
183 | + vm.error = ''; | |
184 | + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( | |
185 | + () => { | |
186 | + vm.executingUpdateValue = false; | |
187 | + if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) { | |
188 | + rpcUpdateValue(vm.scheduledValue); | |
189 | + } | |
190 | + }, | |
191 | + () => { | |
192 | + vm.executingUpdateValue = false; | |
193 | + var errorText = vm.ctx.defaultSubscription.rpcErrorText; | |
194 | + onError(errorText); | |
195 | + } | |
196 | + ); | |
197 | + } | |
198 | +} | ... | ... |
ui/src/app/widget/lib/rpc/round-switch.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-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> | ... | ... |