GPIO widgets
This commit is contained in:
		
							parent
							
								
									7261c75c61
								
							
						
					
					
						commit
						2807c497f0
					
				
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										19
									
								
								ui-ngx/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								ui-ngx/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
@ -198,6 +198,8 @@ export class WidgetContext {
 | 
			
		||||
  servicesMap?: Map<string, Type<any>>;
 | 
			
		||||
 | 
			
		||||
  $injector?: Injector;
 | 
			
		||||
 | 
			
		||||
  ngZone?: NgZone;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IDynamicWidgetComponent {
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -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',
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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>;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								ui-ngx/src/app/shared/components/led-light.component.html
									
									
									
									
									
										Normal file
									
								
							@ -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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								ui-ngx/src/app/shared/components/led-light.component.ts
									
									
									
									
									
										Normal file
									
								
							@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -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,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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user