Merge pull request #220 from thingsboard/feature/TB-70
TB-70: RPC Widgets: LED indicator.
This commit is contained in:
commit
d77dd0f8fc
@ -84,6 +84,22 @@
|
||||
"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}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -17,9 +17,11 @@
|
||||
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,
|
||||
tbRoundSwitch
|
||||
tbRoundSwitch,
|
||||
tbLedIndicator
|
||||
]).name;
|
||||
|
||||
201
ui/src/app/widget/lib/rpc/led-indicator.directive.js
Normal file
201
ui/src/app/widget/lib/rpc/led-indicator.directive.js
Normal file
@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
78
ui/src/app/widget/lib/rpc/led-indicator.scss
Normal file
78
ui/src/app/widget/lib/rpc/led-indicator.scss
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
ui/src/app/widget/lib/rpc/led-indicator.tpl.html
Normal file
32
ui/src/app/widget/lib/rpc/led-indicator.tpl.html
Normal file
@ -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>
|
||||
@ -76,7 +76,6 @@ function RoundSwitchController($element, $scope, utils) {
|
||||
|
||||
vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : '';
|
||||
vm.showTitle = vm.title && vm.title.length ? true : false;
|
||||
vm.showOnOffLabels = angular.isDefined(vm.ctx.settings.showOnOffLabels) ? vm.ctx.settings.showOnOffLabels : true;
|
||||
vm.ctx.resize = resize;
|
||||
$scope.$applyAsync(() => {
|
||||
resize();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user