Merge pull request #220 from thingsboard/feature/TB-70

TB-70: RPC Widgets: LED indicator.
This commit is contained in:
Igor Kulikov 2017-07-26 01:40:37 +03:00 committed by GitHub
commit d77dd0f8fc
6 changed files with 330 additions and 2 deletions

View File

@ -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}"
}
}
]
}

View File

@ -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;

View 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);
}
);
}
}

View 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));
}
}
}
}

View 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>

View File

@ -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();