Browse Source

GPIO widgets

pull/2411/head
Igor Kulikov 7 years ago
parent
commit
2807c497f0
  1. 20
      application/src/main/data/json/system/widget_bundles/gpio_widgets.json
  2. 19
      ui-ngx/package-lock.json
  3. 2
      ui-ngx/package.json
  4. 5
      ui-ngx/src/app/core/api/widget-subscription.ts
  5. 7
      ui-ngx/src/app/core/interceptors/global-http-interceptor.ts
  6. 2
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  7. 23
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  8. 2
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  9. 22
      ui-ngx/src/app/modules/home/pages/dashboard/states/default-state-controller.component.ts
  10. 22
      ui-ngx/src/app/modules/home/pages/dashboard/states/entity-state-controller.component.ts
  11. 17
      ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts
  12. 1
      ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx
  13. 4
      ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts
  14. 18
      ui-ngx/src/app/shared/components/led-light.component.html
  15. 124
      ui-ngx/src/app/shared/components/led-light.component.ts
  16. 3
      ui-ngx/src/app/shared/shared.module.ts
  17. 2
      ui-ngx/src/tsconfig.app.json

20
application/src/main/data/json/system/widget_bundles/gpio_widgets.json

File diff suppressed because one or more lines are too long

19
ui-ngx/package-lock.json

@ -3640,6 +3640,12 @@
"integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
"dev": true
},
"@types/raphael": {
"version": "2.1.30",
"resolved": "https://registry.npmjs.org/@types/raphael/-/raphael-2.1.30.tgz",
"integrity": "sha1-dsvqSlVrol6xxtf6XnGsSOcvgc8=",
"dev": true
},
"@types/react": {
"version": "16.9.16",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.16.tgz",
@ -6620,6 +6626,11 @@
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
"eve-raphael": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/eve-raphael/-/eve-raphael-0.5.0.tgz",
"integrity": "sha1-F8dUt5K+7z+maE15z1pHxjxM2jA="
},
"eventemitter3": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
@ -10955,6 +10966,14 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"dev": true
},
"raphael": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/raphael/-/raphael-2.3.0.tgz",
"integrity": "sha512-w2yIenZAQnp257XUWGni4bLMVxpUpcIl7qgxEgDIXtmSypYtlNxfXWpOBxs7LBTps5sDwhRnrToJrMUrivqNTQ==",
"requires": {
"eve-raphael": "0.5.0"
}
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",

2
ui-ngx/package.json

@ -66,6 +66,7 @@
"ngx-translate-messageformat-compiler": "^4.5.0",
"objectpath": "^1.2.2",
"prop-types": "^15.7.2",
"raphael": "^2.3.0",
"rc-select": "^9.2.1",
"react": "^16.12.0",
"react-ace": "^8.0.0",
@ -97,6 +98,7 @@
"@types/js-beautify": "^1.8.1",
"@types/jstree": "^3.3.39",
"@types/node": "~12.12.17",
"@types/raphael": "^2.1.30",
"@types/react": "^16.9.16",
"@types/react-dom": "^16.9.4",
"@types/tinycolor2": "^1.4.2",

5
ui-ngx/src/app/core/api/widget-subscription.ts

@ -552,8 +552,9 @@ export class WidgetSubscription implements IWidgetSubscription {
}, 500);
} else {
this.executingSubjects.push(rpcSubject);
const targetSendFunction = oneWayElseTwoWay ? this.ctx.deviceService.sendOneWayRpcCommand : this.ctx.deviceService.sendTwoWayRpcCommand;
targetSendFunction(this.targetDeviceId, requestBody).subscribe((responseBody) => {
(oneWayElseTwoWay ? this.ctx.deviceService.sendOneWayRpcCommand(this.targetDeviceId, requestBody) :
this.ctx.deviceService.sendTwoWayRpcCommand(this.targetDeviceId, requestBody))
.subscribe((responseBody) => {
this.rpcRejection = null;
this.rpcErrorText = null;
const index = this.executingSubjects.indexOf(rpcSubject);

7
ui-ngx/src/app/core/interceptors/global-http-interceptor.ts

@ -47,7 +47,8 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
private AUTH_HEADER_NAME = 'X-Authorization';
private internalUrlPrefixes = [
'/api/auth/token'
'/api/auth/token',
'/api/plugins/rpc'
];
private activeRequests = 0;
@ -125,8 +126,8 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
const ignoreErrors = config.ignoreErrors;
const resendRequest = config.resendRequest;
const errorCode = errorResponse.error ? errorResponse.error.errorCode : null;
if (errorResponse.error.refreshTokenPending || errorResponse.status === 401) {
if (errorResponse.error.refreshTokenPending || errorCode && errorCode === Constants.serverErrorCode.jwtTokenExpired) {
if (errorResponse.error && errorResponse.error.refreshTokenPending || errorResponse.status === 401) {
if (errorResponse.error && errorResponse.error.refreshTokenPending || errorCode && errorCode === Constants.serverErrorCode.jwtTokenExpired) {
return this.refreshTokenAndRetry(req, next);
} else if (errorCode !== Constants.serverErrorCode.credentialsExpired) {
unhandled = true;

2
ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts

@ -201,7 +201,7 @@ export class WidgetComponentService {
}
if (widgetControllerDescriptor) {
const widgetNamespace = `widget-type-${(isSystem ? 'sys-' : '')}${bundleAlias}-${widgetInfo.alias}`;
this.loadWidgetResources(widgetInfo, widgetNamespace, [WidgetComponentsModule]).subscribe(
this.loadWidgetResources(widgetInfo, widgetNamespace, [SharedModule, WidgetComponentsModule]).subscribe(
() => {
if (widgetControllerDescriptor.settingsSchema) {
widgetInfo.typeSettingsSchema = widgetControllerDescriptor.settingsSchema;

23
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -27,18 +27,19 @@ import {
NgZone,
OnChanges,
OnDestroy,
OnInit, ReflectiveInjector,
SimpleChanges, Type,
OnInit,
SimpleChanges,
Type,
ViewChild,
ViewContainerRef,
ViewEncapsulation
} from '@angular/core';
import { DashboardWidget, IDashboardComponent } from '@home/models/dashboard-component.models';
import { DashboardWidget } from '@home/models/dashboard-component.models';
import {
Datasource,
defaultLegendConfig,
LegendConfig,
LegendData,
LegendDirection,
LegendPosition,
Widget,
WidgetActionDescriptor,
@ -46,8 +47,7 @@ import {
WidgetActionType,
WidgetResource,
widgetType,
WidgetTypeParameters,
defaultLegendConfig
WidgetTypeParameters
} from '@shared/models/widget.models';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
@ -101,6 +101,7 @@ ServicesMap.set('assetService', AssetService);
ServicesMap.set('dialogs', DialogService);
ServicesMap.set('customDialog', CustomDialogService);
ServicesMap.set('date', DatePipe);
ServicesMap.set('utils', UtilsService);
@Component({
selector: 'tb-widget',
@ -252,6 +253,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.widgetContext = this.dashboardWidget.widgetContext;
this.widgetContext.changeDetector = this.cd;
this.widgetContext.ngZone = this.ngZone;
this.widgetContext.servicesMap = ServicesMap;
this.widgetContext.isEdit = this.isEdit;
this.widgetContext.isMobile = this.isMobile;
@ -533,7 +535,6 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
private reInitImpl() {
this.onDestroy();
this.configureDynamicWidgetComponent();
if (!this.typeParameters.useCustomDatasources) {
this.createDefaultSubscription().subscribe(
() => {
@ -541,6 +542,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.onDestroy();
} else {
this.subscriptionInited = true;
this.configureDynamicWidgetComponent();
this.cd.detectChanges();
this.onInit();
}
},
@ -555,6 +558,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
);
} else {
this.subscriptionInited = true;
this.configureDynamicWidgetComponent();
this.cd.detectChanges();
this.onInit();
}
}
@ -826,6 +831,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
if (this.dynamicWidgetComponent) {
this.dynamicWidgetComponent.rpcEnabled = subscription.rpcEnabled;
this.dynamicWidgetComponent.executingRpcRequest = subscription.executingRpcRequest;
this.cd.detectChanges();
}
},
onRpcSuccess: (subscription) => {
@ -833,6 +839,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.dynamicWidgetComponent.executingRpcRequest = subscription.executingRpcRequest;
this.dynamicWidgetComponent.rpcErrorText = subscription.rpcErrorText;
this.dynamicWidgetComponent.rpcRejection = subscription.rpcRejection;
this.cd.detectChanges();
}
},
onRpcFailed: (subscription) => {
@ -840,12 +847,14 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
this.dynamicWidgetComponent.executingRpcRequest = subscription.executingRpcRequest;
this.dynamicWidgetComponent.rpcErrorText = subscription.rpcErrorText;
this.dynamicWidgetComponent.rpcRejection = subscription.rpcRejection;
this.cd.detectChanges();
}
},
onRpcErrorCleared: (subscription) => {
if (this.dynamicWidgetComponent) {
this.dynamicWidgetComponent.rpcErrorText = null;
this.dynamicWidgetComponent.rpcRejection = null;
this.cd.detectChanges();
}
}
};

2
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -198,6 +198,8 @@ export class WidgetContext {
servicesMap?: Map<string, Type<any>>;
$injector?: Injector;
ngZone?: NgZone;
}
export interface IDynamicWidgetComponent {

22
ui-ngx/src/app/modules/home/pages/dashboard/states/default-state-controller.component.ts

@ -14,22 +14,11 @@
/// limitations under the License.
///
import {
Component,
OnInit,
ViewEncapsulation,
Input,
OnDestroy,
OnChanges,
SimpleChanges,
NgZone
} from '@angular/core';
import { IStateController, StateParams, StateObject } from '@core/api/widget-api.models';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { StateObject, StateParams } from '@core/api/widget-api.models';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subscription, of } from 'rxjs';
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models';
import { DashboardState } from '@shared/models/dashboard.models';
import { IStateControllerComponent, StateControllerState } from './state-controller.models';
import { StateControllerState } from './state-controller.models';
import { StateControllerComponent } from './state-controller.component';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { EntityId } from '@app/shared/models/id/entity-id';
@ -37,8 +26,6 @@ import { UtilsService } from '@core/services/utils.service';
import { base64toObj, objToBase64 } from '@app/core/utils';
import { DashboardUtilsService } from '@core/services/dashboard-utils.service';
import { EntityService } from '@core/http/entity.service';
import { EntityType } from '@shared/models/entity-type.models';
import { map } from 'rxjs/operators';
@Component({
selector: 'tb-default-state-controller',
@ -49,11 +36,12 @@ export class DefaultStateControllerComponent extends StateControllerComponent im
constructor(protected router: Router,
protected route: ActivatedRoute,
protected ngZone: NgZone,
protected statesControllerService: StatesControllerService,
private utils: UtilsService,
private entityService: EntityService,
private dashboardUtils: DashboardUtilsService) {
super(router, route, statesControllerService);
super(router, route, ngZone, statesControllerService);
}
ngOnInit(): void {

22
ui-ngx/src/app/modules/home/pages/dashboard/states/entity-state-controller.component.ts

@ -14,22 +14,11 @@
/// limitations under the License.
///
import {
Component,
OnInit,
ViewEncapsulation,
Input,
OnDestroy,
OnChanges,
SimpleChanges,
NgZone
} from '@angular/core';
import { IStateController, StateParams, StateObject } from '@core/api/widget-api.models';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { StateObject, StateParams } from '@core/api/widget-api.models';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subscription, of } from 'rxjs';
import { IDashboardController } from '@home/pages/dashboard/dashboard-page.models';
import { DashboardState } from '@shared/models/dashboard.models';
import { IStateControllerComponent, StateControllerState } from './state-controller.models';
import { Observable, of } from 'rxjs';
import { StateControllerState } from './state-controller.models';
import { StateControllerComponent } from './state-controller.component';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { EntityId } from '@app/shared/models/id/entity-id';
@ -51,11 +40,12 @@ export class EntityStateControllerComponent extends StateControllerComponent imp
constructor(protected router: Router,
protected route: ActivatedRoute,
protected ngZone: NgZone,
protected statesControllerService: StatesControllerService,
private utils: UtilsService,
private entityService: EntityService,
private dashboardUtils: DashboardUtilsService) {
super(router, route, statesControllerService);
super(router, route, ngZone, statesControllerService);
}
ngOnInit(): void {

17
ui-ngx/src/app/modules/home/pages/dashboard/states/state-controller.component.ts

@ -18,7 +18,7 @@ import { IStateControllerComponent, StateControllerState } from '@home/pages/das
import { IDashboardController } from '../dashboard-page.models';
import { DashboardState } from '@app/shared/models/dashboard.models';
import { Subscription } from 'rxjs';
import { OnDestroy, OnInit } from '@angular/core';
import { NgZone, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { StatesControllerService } from '@home/pages/dashboard/states/states-controller.service';
import { EntityId } from '@app/shared/models/id/entity-id';
@ -91,6 +91,7 @@ export abstract class StateControllerComponent implements IStateControllerCompon
constructor(protected router: Router,
protected route: ActivatedRoute,
protected ngZone: NgZone,
protected statesControllerService: StatesControllerService) {
}
@ -121,12 +122,14 @@ export abstract class StateControllerComponent implements IStateControllerCompon
protected updateStateParam(newState: string) {
this.currentState = newState;
const queryParams: Params = { state: this.currentState };
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams,
queryParamsHandling: 'merge',
this.ngZone.run(() => {
this.router.navigate(
[],
{
relativeTo: this.route,
queryParams,
queryParamsHandling: 'merge',
});
});
}

1
ui-ngx/src/app/shared/components/json-form/react/json-form-array.tsx

@ -157,6 +157,7 @@ class ThingsboardArray extends React.Component<JsonFormFieldProps, ThingsboardAr
addButton = <Button variant='contained'
color='primary'
startIcon={<AddIcon/>}
style={{marginBottom: '8px'}}
onClick={this.onAppend}>{this.props.form.add || 'New'}</Button>;
}

4
ui-ngx/src/app/shared/components/json-form/react/json-form-utils.ts

@ -314,7 +314,7 @@ function defaultFormDefinition(name: string, schema: any, options: DefaultsFormO
const rules = defaults[stripNullType(schema.type)];
if (rules) {
let def;
rules.forEach((rule) => {
for (const rule of rules) {
def = rule(name, schema, options);
if (def) {
@ -324,7 +324,7 @@ function defaultFormDefinition(name: string, schema: any, options: DefaultsFormO
}
return def;
}
});
}
}
}

18
ui-ngx/src/app/shared/components/led-light.component.html

@ -0,0 +1,18 @@
<!--
Copyright © 2016-2019 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 id="canvas_container" [ngStyle]="{width: size + 'px', height: size + 'px'}"></div>

124
ui-ngx/src/app/shared/components/led-light.component.ts

@ -0,0 +1,124 @@
///
/// Copyright © 2016-2019 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 { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import Raphael from 'raphael';
import * as tinycolor_ from 'tinycolor2';
const tinycolor = tinycolor_;
interface CircleElement extends RaphaelElement {
theGlow?: RaphaelSet;
}
@Component({
selector: 'tb-led-light',
templateUrl: './led-light.component.html',
styleUrls: []
})
export class LedLightComponent implements OnInit, AfterViewInit, OnChanges {
@Input() size: number;
@Input() colorOn: string;
@Input() colorOff: string;
@Input() offOpacity: string;
private enabledValue: boolean;
get enabled(): boolean {
return this.enabledValue;
}
@Input()
set enabled(value: boolean) {
this.enabledValue = coerceBooleanProperty(value);
}
private canvasSize: number;
private radius: number;
private glowSize: number;
private glowColor: string;
private paper: RaphaelPaper;
private circleElement: CircleElement;
constructor(private elementRef: ElementRef<HTMLElement>) {
}
ngOnInit(): void {
this.offOpacity = this.offOpacity || '0.4';
this.glowColor = tinycolor(this.colorOn).lighten().toHexString();
}
ngAfterViewInit(): void {
this.update();
}
ngOnChanges(changes: SimpleChanges): void {
for (const propName of Object.keys(changes)) {
const change = changes[propName];
if (!change.firstChange && change.currentValue !== change.previousValue) {
if (propName === 'enabled') {
this.draw();
} else if (propName === 'size') {
this.update();
}
}
}
}
private update() {
this.size = this.size || 50;
this.canvasSize = this.size;
this.radius = this.canvasSize / 4;
this.glowSize = this.radius / 5;
if (this.paper) {
this.paper.remove();
}
this.paper = Raphael($('#canvas_container', this.elementRef.nativeElement)[0], this.canvasSize, this.canvasSize);
const center = this.canvasSize / 2;
this.circleElement = this.paper.circle(center, center, this.radius);
this.draw();
}
private draw() {
if (this.enabled) {
this.circleElement.attr('fill', this.colorOn);
this.circleElement.attr('stroke', this.colorOn);
this.circleElement.attr('opacity', '1');
if (this.circleElement.theGlow) {
this.circleElement.theGlow.remove();
}
this.circleElement.theGlow = this.circleElement.glow(
{
color: this.glowColor,
width: this.radius + this.glowSize,
opacity: 0.8,
fill: true
});
} else {
if (this.circleElement.theGlow) {
this.circleElement.theGlow.remove();
}
this.circleElement.attr('fill', this.colorOff);
this.circleElement.attr('stroke', this.colorOff);
this.circleElement.attr('opacity', this.offOpacity);
}
}
}

3
ui-ngx/src/app/shared/shared.module.ts

@ -121,6 +121,7 @@ import { KeyValMapComponent } from './components/kv-map.component';
import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component';
import { TbHotkeysDirective } from '@shared/components/hotkeys.directive';
import { NavTreeComponent } from '@shared/components/nav-tree.component';
import { LedLightComponent } from '@shared/components/led-light.component';
@NgModule({
providers: [
@ -200,6 +201,7 @@ import { NavTreeComponent } from '@shared/components/nav-tree.component';
MessageTypeAutocompleteComponent,
KeyValMapComponent,
NavTreeComponent,
LedLightComponent,
NospacePipe,
MillisecondsToTimeStringPipe,
EnumToArrayPipe,
@ -349,6 +351,7 @@ import { NavTreeComponent } from '@shared/components/nav-tree.component';
MessageTypeAutocompleteComponent,
KeyValMapComponent,
NavTreeComponent,
LedLightComponent,
NospacePipe,
MillisecondsToTimeStringPipe,
EnumToArrayPipe,

2
ui-ngx/src/tsconfig.app.json

@ -2,7 +2,7 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", "react", "react-dom", "jstree"]
"types": ["node", "jquery", "flot", "tooltipster", "tinycolor2", "js-beautify", "react", "react-dom", "jstree", "raphael"]
},
"exclude": [
"test.ts",

Loading…
Cancel
Save