2017-07-26 01:38:07 +03:00
|
|
|
/*
|
|
|
|
|
* 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*/
|
2017-07-26 16:27:50 +03:00
|
|
|
function LedIndicatorController($element, $scope, $timeout, utils, types) {
|
2017-07-26 01:38:07 +03:00
|
|
|
let vm = this;
|
|
|
|
|
|
|
|
|
|
vm.showTitle = false;
|
|
|
|
|
vm.value = false;
|
|
|
|
|
vm.error = '';
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
const checkStatusPollingInterval = 10000;
|
|
|
|
|
|
|
|
|
|
vm.subscriptionOptions = {
|
|
|
|
|
callbacks: {
|
|
|
|
|
onDataUpdated: onDataUpdated,
|
|
|
|
|
onDataUpdateError: onDataUpdateError,
|
|
|
|
|
dataLoading: () => {}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-07-26 01:38:07 +03:00
|
|
|
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;
|
2017-07-26 16:27:50 +03:00
|
|
|
if (vm.checkStatusTimeoutHandle) {
|
|
|
|
|
$timeout.cancel(vm.checkStatusTimeoutHandle);
|
|
|
|
|
}
|
|
|
|
|
if (vm.subscription) {
|
|
|
|
|
vm.ctx.subscriptionApi.removeSubscription(vm.subscription.id);
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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';
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
vm.valueAttribute = angular.isDefined(vm.ctx.settings.valueAttribute) ? vm.ctx.settings.valueAttribute : 'value';
|
|
|
|
|
|
2017-07-26 01:38:07 +03:00
|
|
|
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;
|
|
|
|
|
}
|
2017-09-06 20:43:35 +03:00
|
|
|
vm.retrieveValueMethod = 'attribute';
|
|
|
|
|
if (vm.ctx.settings.retrieveValueMethod && vm.ctx.settings.retrieveValueMethod.length) {
|
|
|
|
|
vm.retrieveValueMethod = vm.ctx.settings.retrieveValueMethod;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vm.parseValueFunction = (data) => data ? true : false;
|
|
|
|
|
if (vm.ctx.settings.parseValueFunction && vm.ctx.settings.parseValueFunction.length) {
|
|
|
|
|
try {
|
|
|
|
|
vm.parseValueFunction = new Function('data', vm.ctx.settings.parseValueFunction);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
vm.parseValueFunction = (data) => data ? true : false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
vm.checkStatusMethod = 'checkStatus';
|
|
|
|
|
if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) {
|
|
|
|
|
vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod;
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
|
|
|
|
if (!rpcEnabled) {
|
|
|
|
|
onError('Target device is not set!');
|
|
|
|
|
} else {
|
|
|
|
|
if (!vm.isSimulated) {
|
2017-07-26 16:27:50 +03:00
|
|
|
rpcCheckStatus();
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
function rpcCheckStatus() {
|
2017-07-26 01:38:07 +03:00
|
|
|
if (vm.destroyed) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
vm.error = '';
|
2017-07-26 16:27:50 +03:00
|
|
|
vm.ctx.controlApi.sendTwoWayCommand(vm.checkStatusMethod, null, vm.requestTimeout).then(
|
2017-07-26 01:38:07 +03:00
|
|
|
(responseBody) => {
|
2017-07-26 16:27:50 +03:00
|
|
|
var status = responseBody ? true : false;
|
|
|
|
|
if (status) {
|
|
|
|
|
if (vm.checkStatusTimeoutHandle) {
|
|
|
|
|
$timeout.cancel(vm.checkStatusTimeoutHandle);
|
|
|
|
|
vm.checkStatusTimeoutHandle = null;
|
|
|
|
|
}
|
|
|
|
|
subscribeForValue();
|
|
|
|
|
} else {
|
|
|
|
|
var errorText = 'Unknown device status!';
|
|
|
|
|
onError(errorText);
|
|
|
|
|
if (vm.checkStatusTimeoutHandle) {
|
|
|
|
|
$timeout.cancel(vm.checkStatusTimeoutHandle);
|
|
|
|
|
}
|
|
|
|
|
vm.checkStatusTimeoutHandle = $timeout(rpcCheckStatus, checkStatusPollingInterval);
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
() => {
|
|
|
|
|
var errorText = vm.ctx.defaultSubscription.rpcErrorText;
|
|
|
|
|
onError(errorText);
|
2017-07-26 16:27:50 +03:00
|
|
|
if (vm.checkStatusTimeoutHandle) {
|
|
|
|
|
$timeout.cancel(vm.checkStatusTimeoutHandle);
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
2017-07-26 16:27:50 +03:00
|
|
|
vm.checkStatusTimeoutHandle = $timeout(rpcCheckStatus, checkStatusPollingInterval);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function subscribeForValue() {
|
|
|
|
|
var subscriptionsInfo = [{
|
|
|
|
|
type: types.datasourceType.entity,
|
|
|
|
|
entityType: types.entityType.device,
|
2017-09-06 20:43:35 +03:00
|
|
|
entityId: vm.ctx.defaultSubscription.targetDeviceId
|
2017-07-26 16:27:50 +03:00
|
|
|
}];
|
2017-09-06 20:43:35 +03:00
|
|
|
|
|
|
|
|
if (vm.retrieveValueMethod == 'attribute') {
|
|
|
|
|
subscriptionsInfo[0].attributes = [
|
|
|
|
|
{name: vm.valueAttribute}
|
|
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
subscriptionsInfo[0].timeseries = [
|
|
|
|
|
{name: vm.valueAttribute}
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
vm.ctx.subscriptionApi.createSubscriptionFromInfo (
|
|
|
|
|
types.widgetType.latest.value, subscriptionsInfo, vm.subscriptionOptions, false, true).then(
|
|
|
|
|
function(subscription) {
|
|
|
|
|
vm.subscription = subscription;
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 16:27:50 +03:00
|
|
|
function onDataUpdated(subscription, apply) {
|
|
|
|
|
var value = false;
|
|
|
|
|
var data = subscription.data;
|
|
|
|
|
if (data.length) {
|
|
|
|
|
var keyData = data[0];
|
|
|
|
|
if (keyData && keyData.data && keyData.data[0]) {
|
|
|
|
|
var attrValue = keyData.data[0][1];
|
|
|
|
|
if (attrValue) {
|
|
|
|
|
var parsed = null;
|
|
|
|
|
try {
|
2017-09-06 20:43:35 +03:00
|
|
|
parsed = vm.parseValueFunction(angular.fromJson(attrValue));
|
2017-07-26 16:27:50 +03:00
|
|
|
} catch (e){/**/}
|
|
|
|
|
value = parsed ? true : false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setValue(value);
|
|
|
|
|
if (apply) {
|
|
|
|
|
$scope.$digest();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onDataUpdateError(subscription, e) {
|
|
|
|
|
var exceptionData = utils.parseException(e);
|
|
|
|
|
var errorText = exceptionData.name;
|
|
|
|
|
if (exceptionData.message) {
|
|
|
|
|
errorText += ': ' + exceptionData.message;
|
|
|
|
|
}
|
|
|
|
|
onError(errorText);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-26 01:38:07 +03:00
|
|
|
}
|