Browse Source

Merge branch 'master' into lwm2m_create_coap_resource

# Conflicts:
#	ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html
pull/4680/head
nickAS21 5 years ago
parent
commit
87874c3eb6
  1. 28
      ui-ngx/src/app/core/api/data-aggregator.ts
  2. 14
      ui-ngx/src/app/core/api/entity-data-subscription.ts
  3. 1
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  4. 25
      ui-ngx/src/app/core/services/mobile.service.ts
  5. 39
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  6. 194
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html
  7. 12
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss
  8. 96
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts
  9. 1
      ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html
  10. 20
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss
  11. 2
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts
  12. 4
      ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss
  13. 21
      ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts
  14. 17
      ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts
  15. 209
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html
  16. 40
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  17. 57
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  18. 6
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html
  19. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  20. 3
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss
  21. 3
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts
  22. 25
      ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts
  23. 28
      ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
  24. 2
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts
  25. 2
      ui-ngx/src/app/shared/components/color-input.component.html
  26. 2
      ui-ngx/src/app/shared/models/dashboard.models.ts
  27. 2
      ui-ngx/src/app/shared/models/window-message.model.ts
  28. 7
      ui-ngx/src/assets/locale/locale.constant-en_US.json

28
ui-ngx/src/app/core/api/data-aggregator.ts

@ -16,16 +16,17 @@
import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models';
import {
AggregationType, calculateIntervalComparisonEndTime,
calculateIntervalEndTime, calculateIntervalStartEndTime,
AggregationType,
calculateIntervalComparisonEndTime,
calculateIntervalEndTime,
calculateIntervalStartEndTime,
getCurrentTime,
getCurrentTimeForComparison, getTime,
getTime,
SubscriptionTimewindow
} from '@shared/models/time/time.models';
import { UtilsService } from '@core/services/utils.service';
import { deepClone } from '@core/utils';
import { deepClone, isNumeric } from '@core/utils';
import Timeout = NodeJS.Timeout;
import * as moment_ from 'moment';
export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void;
@ -407,24 +408,11 @@ export class DataAggregator {
}
}
private isNumeric(val: any): boolean {
return (val - parseFloat( val ) + 1) >= 0;
}
private convertValue(val: string): any {
if (!this.noAggregation || val && this.isNumeric(val)) {
if (!this.noAggregation || val && isNumeric(val) && Number(val).toString() === val) {
return Number(val);
} else {
return val;
}
}
private getCurrentTime() {
if (this.subsTw.timeForComparison) {
return getCurrentTimeForComparison(this.subsTw.timeForComparison as moment_.unitOfTime.DurationConstructor, this.subsTw.timezone);
} else {
return getCurrentTime(this.subsTw.timezone);
}
return val;
}
}

14
ui-ngx/src/app/core/api/entity-data-subscription.ts

@ -38,7 +38,7 @@ import {
} from '@shared/models/telemetry/telemetry.models';
import { UtilsService } from '@core/services/utils.service';
import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service';
import { deepClone, isDefined, isDefinedAndNotNull, isObject, objectHashCode } from '@core/utils';
import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isObject, objectHashCode } from '@core/utils';
import { PageData } from '@shared/models/page/page-data';
import { DataAggregator } from '@core/api/data-aggregator';
import { NULL_UUID } from '@shared/models/id/has-uuid';
@ -667,8 +667,7 @@ export class EntityDataSubscription {
if (prevDataCb) {
dataAggregator.updateOnDataCb(prevDataCb);
}
}
if (!this.history && !isUpdate) {
} else if (!this.history && !isUpdate) {
this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb);
}
}
@ -743,16 +742,11 @@ export class EntityDataSubscription {
}
}
private isNumeric(val: any): boolean {
return (val - parseFloat( val ) + 1) >= 0;
}
private convertValue(val: string): any {
if (val && this.isNumeric(val) && Number(val).toString() === val) {
if (val && isNumeric(val) && Number(val).toString() === val) {
return Number(val);
} else {
return val;
}
return val;
}
private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData {

1
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -231,7 +231,6 @@ export class DashboardUtilsService {
private createDefaultGridSettings(): GridSettings {
return {
backgroundColor: '#eeeeee',
color: 'rgba(0,0,0,0.870588)',
columns: 24,
margin: 10,
backgroundSizeMode: '100%'

25
ui-ngx/src/app/core/services/mobile.service.ts

@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service';
const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler';
const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler';
const dashboardLayoutHandler = 'tbMobileDashboardLayoutHandler';
const navigationHandler = 'tbMobileNavigationHandler';
const mobileHandler = 'tbMobileHandler';
@ -43,6 +44,7 @@ export class MobileService {
private reloadUserObservable: Observable<boolean>;
private lastDashboardId: string;
private toggleLayoutFunction: () => void;
constructor(@Inject(WINDOW) private window: Window,
private router: Router,
@ -65,12 +67,26 @@ export class MobileService {
}
}
public onDashboardLoaded() {
public onDashboardLoaded(hasRightLayout: boolean, rightLayoutOpened: boolean) {
if (this.mobileApp) {
this.mobileChannel.callHandler(dashboardLoadedHandler);
this.mobileChannel.callHandler(dashboardLoadedHandler, hasRightLayout, rightLayoutOpened);
}
}
public onDashboardRightLayoutChanged(opened: boolean) {
if (this.mobileApp) {
this.mobileChannel.callHandler(dashboardLayoutHandler, opened);
}
}
public registerToggleLayoutFunction(toggleLayoutFunction: () => void) {
this.toggleLayoutFunction = toggleLayoutFunction;
}
public unregisterToggleLayoutFunction() {
this.toggleLayoutFunction = null;
}
public handleWidgetMobileAction<T extends MobileActionResult>(type: WidgetMobileActionType, ...args: any[]):
Observable<WidgetMobileActionResult<T>> {
if (this.mobileApp) {
@ -110,6 +126,11 @@ export class MobileService {
const reloadUserMessage: ReloadUserMessage = message.data;
this.reloadUser(reloadUserMessage);
break;
case 'toggleDashboardLayout':
if (this.toggleLayoutFunction) {
this.toggleLayoutFunction();
}
break;
}
}
}

39
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
@Input()
currentState: string;
private hideToolbarValue = false;
@Input()
hideToolbar: boolean;
set hideToolbar(hideToolbar: boolean) {
this.hideToolbarValue = hideToolbar;
}
get hideToolbar(): boolean {
return (this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit;
}
@Input()
syncStateWithQueryParam = true;
@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
return !this.widgetEditMode && !this.hideToolbar &&
(this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch());
}
set toolbarOpened(toolbarOpened: boolean) {
}
@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.dashboardCtx.aliasController.updateAliases();
setTimeout(() => {
this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName());
this.mobileService.onDashboardLoaded();
this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened);
});
}
}
@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.isMobile = !state.matches;
}
));
if (this.isMobileApp) {
this.mobileService.registerToggleLayoutFunction(() => {
setTimeout(() => {
this.toggleLayouts();
this.cd.detectChanges();
});
});
}
}
private init(data: any) {
@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
ngOnDestroy(): void {
if (this.isMobileApp) {
this.mobileService.unregisterToggleLayoutFunction();
}
this.rxSubscriptions.forEach((subscription) => {
subscription.unsubscribe();
});
@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
}
private hideToolbarSetting(): boolean {
if (this.dashboard.configuration.settings &&
isDefined(this.dashboard.configuration.settings.hideToolbar)) {
return this.dashboard.configuration.settings.hideToolbar;
} else {
return false;
}
}
public displayTitle(): boolean {
if (this.dashboard.configuration.settings &&
isDefined(this.dashboard.configuration.settings.showTitle)) {
@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
}
public showRightLayoutSwitch(): boolean {
return this.isMobile && this.layouts.right.show;
return this.isMobile && !this.isMobileApp && this.layouts.right.show;
}
public toggleLayouts() {
this.isRightLayoutOpened = !this.isRightLayoutOpened;
this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened);
}
public openRightLayout() {
this.isRightLayoutOpened = true;
this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened);
}
public mainLayoutWidth(): string {
@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
this.updateLayouts(layoutsData);
}
setTimeout(() => {
this.mobileService.onDashboardLoaded();
this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened);
});
}

194
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<form (ngSubmit)="save()">
<form (ngSubmit)="save()" style="width: 750px;">
<mat-toolbar color="primary">
<h2 translate>{{settings ? 'dashboard.settings' : 'layout.settings'}}</h2>
<span fxFlex></span>
@ -28,8 +28,8 @@
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
</mat-progress-bar>
<div mat-dialog-content>
<fieldset [disabled]="isLoading$ | async" fxLayout="column" fxLayoutGap="16px">
<div *ngIf="settings" [formGroup]="settingsFormGroup">
<fieldset [disabled]="isLoading$ | async">
<div *ngIf="settings" [formGroup]="settingsFormGroup" fxLayout="column">
<mat-form-field class="mat-block">
<mat-label translate>dashboard.state-controller</mat-label>
<mat-select required formControlName="stateControllerId">
@ -38,112 +38,116 @@
</mat-option>
</mat-select>
</mat-form-field>
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">
<mat-checkbox fxFlex formControlName="toolbarAlwaysOpen">
{{ 'dashboard.toolbar-always-open' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showTitle">
<fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px">
<legend class="group-title" translate>dashboard.title-settings</legend>
<mat-slide-toggle fxFlex formControlName="showTitle">
{{ 'dashboard.display-title' | translate }}
</mat-checkbox>
</mat-slide-toggle>
<tb-color-input fxFlex
label="{{'dashboard.title-color' | translate}}"
icon="format_color_fill"
openOnInput
formControlName="titleColor">
</tb-color-input>
</div>
<div fxLayout="row" fxLayoutAlign="start center" style="margin-bottom: 8px;"
fxLayout.lt-md="column" fxLayoutAlign.lt-md fxLayoutGap="8px">
<mat-checkbox fxFlex formControlName="showDashboardsSelect">
</fieldset>
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
<legend class="group-title" translate>dashboard.dashboard-logo-settings</legend>
<mat-slide-toggle formControlName="showDashboardLogo">
{{ 'dashboard.display-dashboard-logo' | translate }}
</mat-slide-toggle>
<tb-image-input fxFlex
label="{{'dashboard.dashboard-logo-image' | translate}}"
formControlName="dashboardLogoUrl">
</tb-image-input>
</fieldset>
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
<legend class="group-title" translate>dashboard.toolbar-settings</legend>
<mat-slide-toggle formControlName="hideToolbar">
{{ 'dashboard.hide-toolbar' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="toolbarAlwaysOpen">
{{ 'dashboard.toolbar-always-open' | translate }}
</mat-slide-toggle>
<mat-slide-toggle formControlName="showDashboardsSelect">
{{ 'dashboard.display-dashboards-selection' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showEntitiesSelect">
</mat-slide-toggle>
<mat-slide-toggle formControlName="showEntitiesSelect">
{{ 'dashboard.display-entities-selection' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showFilters">
</mat-slide-toggle>
<mat-slide-toggle formControlName="showFilters">
{{ 'dashboard.display-filters' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showDashboardTimewindow">
</mat-slide-toggle>
<mat-slide-toggle formControlName="showDashboardTimewindow">
{{ 'dashboard.display-dashboard-timewindow' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showDashboardExport">
</mat-slide-toggle>
<mat-slide-toggle formControlName="showDashboardExport">
{{ 'dashboard.display-dashboard-export' | translate }}
</mat-checkbox>
<mat-checkbox fxFlex formControlName="showUpdateDashboardImage">
</mat-slide-toggle>
<mat-slide-toggle formControlName="showUpdateDashboardImage">
{{ 'dashboard.display-update-dashboard-image' | translate }}
</mat-checkbox>
</div>
<mat-checkbox formControlName="showDashboardLogo">
{{ 'dashboard.display-dashboard-logo' | translate }}
</mat-checkbox>
<tb-image-input fxFlex *ngIf="settingsFormGroup.get('showDashboardLogo').value"
label="{{'dashboard.dashboard-logo-image' | translate}}"
formControlName="dashboardLogoUrl">
</tb-image-input>
</mat-slide-toggle>
</fieldset>
</div>
<div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup">
<tb-color-input fxFlex
label="{{'layout.color' | translate}}"
icon="format_color_fill"
openOnInput
formControlName="color">
</tb-color-input>
<mat-form-field class="mat-block">
<mat-label translate>dashboard.columns-count</mat-label>
<input matInput formControlName="columns" type="number" step="any" min="10"
max="1000" required>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')">
{{ 'dashboard.columns-count-required' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')">
{{ 'dashboard.min-columns-count-message' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')">
{{ 'dashboard.max-columns-count-message' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>dashboard.widgets-margins</mat-label>
<input matInput formControlName="margin" type="number" step="any" min="0"
max="50" required>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')">
{{ 'dashboard.margin-required' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')">
{{ 'dashboard.min-margin-message' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')">
{{ 'dashboard.max-margin-message' | translate }}
</mat-error>
</mat-form-field>
<mat-checkbox fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;">
{{ 'dashboard.autofill-height' | translate }}
</mat-checkbox>
<tb-color-input fxFlex
label="{{'dashboard.background-color' | translate}}"
icon="format_color_fill"
openOnInput
formControlName="backgroundColor">
</tb-color-input>
<tb-image-input fxFlex
label="{{'dashboard.background-image' | translate}}"
formControlName="backgroundImageUrl">
</tb-image-input>
<mat-form-field class="mat-block">
<mat-label translate>dashboard.background-size-mode</mat-label>
<mat-select formControlName="backgroundSizeMode">
<mat-option value="100%">Fit width</mat-option>
<mat-option value="auto 100%">Fit height</mat-option>
<mat-option value="cover">Cover</mat-option>
<mat-option value="contain">Contain</mat-option>
<mat-option value="auto">Original size</mat-option>
</mat-select>
</mat-form-field>
<small translate>dashboard.mobile-layout</small>
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs fxLayoutGap="8px" style="margin-top: 8px">
<mat-checkbox fxFlex formControlName="mobileAutoFillHeight">
<div *ngIf="gridSettings" [formGroup]="gridSettingsFormGroup" fxLayout="column">
<fieldset class="fields-group" fxLayout="column" fxLayoutGap="8px">
<legend class="group-title" translate>dashboard.layout-settings</legend>
<mat-form-field class="mat-block">
<mat-label translate>dashboard.columns-count</mat-label>
<input matInput formControlName="columns" type="number" step="any" min="10"
max="1000" required>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('required')">
{{ 'dashboard.columns-count-required' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('min')">
{{ 'dashboard.min-columns-count-message' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('columns').hasError('max')">
{{ 'dashboard.max-columns-count-message' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>dashboard.widgets-margins</mat-label>
<input matInput formControlName="margin" type="number" step="any" min="0"
max="50" required>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('required')">
{{ 'dashboard.margin-required' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('min')">
{{ 'dashboard.min-margin-message' | translate }}
</mat-error>
<mat-error *ngIf="gridSettingsFormGroup.get('margin').hasError('max')">
{{ 'dashboard.max-margin-message' | translate }}
</mat-error>
</mat-form-field>
<mat-slide-toggle fxFlex formControlName="autoFillHeight" style="display: block; padding-bottom: 12px;">
{{ 'dashboard.autofill-height' | translate }}
</mat-slide-toggle>
<tb-color-input fxFlex
label="{{'dashboard.background-color' | translate}}"
icon="format_color_fill"
openOnInput
formControlName="backgroundColor">
</tb-color-input>
<tb-image-input fxFlex
label="{{'dashboard.background-image' | translate}}"
formControlName="backgroundImageUrl">
</tb-image-input>
<mat-form-field class="mat-block">
<mat-label translate>dashboard.background-size-mode</mat-label>
<mat-select formControlName="backgroundSizeMode">
<mat-option value="100%">Fit width</mat-option>
<mat-option value="auto 100%">Fit height</mat-option>
<mat-option value="cover">Cover</mat-option>
<mat-option value="contain">Contain</mat-option>
<mat-option value="auto">Original size</mat-option>
</mat-select>
</mat-form-field>
</fieldset>
<fieldset class="fields-group" fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center" fxLayoutGap="8px">
<legend class="group-title" translate>dashboard.mobile-layout</legend>
<mat-slide-toggle fxFlex formControlName="mobileAutoFillHeight">
{{ 'dashboard.autofill-height' | translate }}
</mat-checkbox>
</mat-slide-toggle>
<mat-form-field fxFlex class="mat-block">
<mat-label translate>dashboard.mobile-row-height</mat-label>
<input matInput formControlName="mobileRowHeight" type="number" step="any" min="5"
@ -158,7 +162,7 @@
{{ 'dashboard.max-mobile-row-height-message' | translate }}
</mat-error>
</mat-form-field>
</div>
</fieldset>
</div>
</fieldset>
</div>

12
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss

@ -14,13 +14,19 @@
* limitations under the License.
*/
:host {
small {
white-space: normal;
.fields-group {
padding: 0 8px 8px;
margin: 10px 0;
border: 1px groove rgba(0, 0, 0, .25);
border-radius: 4px;
legend {
color: rgba(0, 0, 0, .7);
}
}
}
:host ::ng-deep {
.mat-checkbox-label {
.mat-slide-toggle-content {
white-space: normal;
}
}

96
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts

@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
this.gridSettings = this.data.gridSettings;
if (this.settings) {
const showTitle = isUndefined(this.settings.showTitle) ? true : this.settings.showTitle;
const showDashboardLogo = isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo;
const hideToolbar = isUndefined(this.settings.hideToolbar) ? false : this.settings.hideToolbar;
this.settingsFormGroup = this.fb.group({
stateControllerId: [isUndefined(this.settings.stateControllerId) ? 'entity' : this.settings.stateControllerId, []],
toolbarAlwaysOpen: [isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen, []],
showTitle: [isUndefined(this.settings.showTitle) ? true : this.settings.showTitle, []],
titleColor: [isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor, []],
showDashboardsSelect: [isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect, []],
showEntitiesSelect: [isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect, []],
showFilters: [isUndefined(this.settings.showFilters) ? true : this.settings.showFilters, []],
showDashboardLogo: [isUndefined(this.settings.showDashboardLogo) ? false : this.settings.showDashboardLogo, []],
dashboardLogoUrl: [isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl, []],
showDashboardTimewindow: [isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow, []],
showDashboardExport: [isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport, []],
showUpdateDashboardImage: [isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage, []]
showTitle: [showTitle, []],
titleColor: [{value: isUndefined(this.settings.titleColor) ? 'rgba(0,0,0,0.870588)' : this.settings.titleColor,
disabled: !showTitle}, []],
showDashboardLogo: [showDashboardLogo, []],
dashboardLogoUrl: [{value: isUndefined(this.settings.dashboardLogoUrl) ? null : this.settings.dashboardLogoUrl,
disabled: !showDashboardLogo}, []],
hideToolbar: [hideToolbar, []],
toolbarAlwaysOpen: [{value: isUndefined(this.settings.toolbarAlwaysOpen) ? true : this.settings.toolbarAlwaysOpen,
disabled: hideToolbar}, []],
showDashboardsSelect: [{value: isUndefined(this.settings.showDashboardsSelect) ? true : this.settings.showDashboardsSelect,
disabled: hideToolbar}, []],
showEntitiesSelect: [{value: isUndefined(this.settings.showEntitiesSelect) ? true : this.settings.showEntitiesSelect,
disabled: hideToolbar}, []],
showFilters: [{value: isUndefined(this.settings.showFilters) ? true : this.settings.showFilters,
disabled: hideToolbar}, []],
showDashboardTimewindow: [{value: isUndefined(this.settings.showDashboardTimewindow) ? true : this.settings.showDashboardTimewindow,
disabled: hideToolbar}, []],
showDashboardExport: [{value: isUndefined(this.settings.showDashboardExport) ? true : this.settings.showDashboardExport,
disabled: hideToolbar}, []],
showUpdateDashboardImage: [
{value: isUndefined(this.settings.showUpdateDashboardImage) ? true : this.settings.showUpdateDashboardImage,
disabled: hideToolbar}, []]
});
this.settingsFormGroup.get('stateControllerId').valueChanges.subscribe(
(stateControllerId: StateControllerId) => {
@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
}
}
);
this.settingsFormGroup.get('showTitle').valueChanges.subscribe(
(showTitleValue: boolean) => {
if (showTitleValue) {
this.settingsFormGroup.get('titleColor').enable();
} else {
this.settingsFormGroup.get('titleColor').disable();
}
}
);
this.settingsFormGroup.get('showDashboardLogo').valueChanges.subscribe(
(showDashboardLogoValue: boolean) => {
if (showDashboardLogoValue) {
this.settingsFormGroup.get('dashboardLogoUrl').enable();
} else {
this.settingsFormGroup.get('dashboardLogoUrl').disable();
}
}
);
this.settingsFormGroup.get('hideToolbar').valueChanges.subscribe(
(hideToolbarValue: boolean) => {
if (hideToolbarValue) {
this.settingsFormGroup.get('toolbarAlwaysOpen').disable();
this.settingsFormGroup.get('showDashboardsSelect').disable();
this.settingsFormGroup.get('showEntitiesSelect').disable();
this.settingsFormGroup.get('showFilters').disable();
this.settingsFormGroup.get('showDashboardTimewindow').disable();
this.settingsFormGroup.get('showDashboardExport').disable();
this.settingsFormGroup.get('showUpdateDashboardImage').disable();
} else {
this.settingsFormGroup.get('toolbarAlwaysOpen').enable();
this.settingsFormGroup.get('showDashboardsSelect').enable();
this.settingsFormGroup.get('showEntitiesSelect').enable();
this.settingsFormGroup.get('showFilters').enable();
this.settingsFormGroup.get('showDashboardTimewindow').enable();
this.settingsFormGroup.get('showDashboardExport').enable();
this.settingsFormGroup.get('showUpdateDashboardImage').enable();
}
}
);
} else {
this.settingsFormGroup = this.fb.group({});
}
if (this.gridSettings) {
const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight;
this.gridSettingsFormGroup = this.fb.group({
color: [this.gridSettings.color || 'rgba(0,0,0,0.870588)', []],
columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]],
margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10,
[Validators.required, Validators.min(0), Validators.max(50)]],
@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
backgroundColor: [this.gridSettings.backgroundColor || 'rgba(0,0,0,0)', []],
backgroundImageUrl: [this.gridSettings.backgroundImageUrl, []],
backgroundSizeMode: [this.gridSettings.backgroundSizeMode || '100%', []],
mobileAutoFillHeight: [isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight, []],
mobileRowHeight: [isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight,
[Validators.required, Validators.min(5), Validators.max(200)]]
mobileAutoFillHeight: [mobileAutoFillHeight, []],
mobileRowHeight: [{ value: isUndefined(this.gridSettings.mobileRowHeight) ? 70 : this.gridSettings.mobileRowHeight,
disabled: mobileAutoFillHeight}, [Validators.required, Validators.min(5), Validators.max(200)]]
});
this.gridSettingsFormGroup.get('mobileAutoFillHeight').valueChanges.subscribe(
(mobileAutoFillHeightValue: boolean) => {
if (mobileAutoFillHeightValue) {
this.gridSettingsFormGroup.get('mobileRowHeight').disable();
} else {
this.gridSettingsFormGroup.get('mobileRowHeight').enable();
}
}
);
} else {
this.gridSettingsFormGroup = this.fb.group({});
}
@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent<DashboardS
let settings: DashboardSettings = null;
let gridSettings: GridSettings = null;
if (this.settings) {
settings = {...this.settings, ...this.settingsFormGroup.value};
settings = {...this.settings, ...this.settingsFormGroup.getRawValue()};
}
if (this.gridSettings) {
gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.value};
gridSettings = {...this.gridSettings, ...this.gridSettingsFormGroup.getRawValue()};
}
this.dialogRef.close({settings, gridSettings});
}

1
ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html

@ -26,7 +26,6 @@
[style.backgroundImage]="backgroundImage"
[ngStyle]="dashboardStyle">
<section *ngIf="layoutCtx.widgets.isEmpty()" fxLayoutAlign="center center"
[ngStyle]="{'color': layoutCtx.gridSettings.color}"
style="display: flex; z-index: 1; pointer-events: none;"
class="mat-headline tb-absolute-fill">
<span *ngIf="!isEdit">

20
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss

@ -1,20 +0,0 @@
/**
* Copyright © 2016-2021 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.
*/
:host ::ng-deep {
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

2
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts

@ -41,7 +41,7 @@ import { Subject } from 'rxjs';
@Component({
selector: 'tb-security-config-lwm2m-server',
templateUrl: './security-config-lwm2m-server.component.html',
styleUrls: ['./security-config-lwm2m-server.component.scss'],
styleUrls: [],
providers: [
{
provide: NG_VALUE_ACCESSOR,

4
ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss

@ -27,8 +27,4 @@
.mat-tab-body {
padding: 16px 0;
}
textarea.mat-input-element.cdk-textarea-autosize {
box-sizing: content-box;
}
}

21
ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
@ -33,6 +33,8 @@ import {
transportPayloadTypeTranslationMap,
} from '@shared/models/device.models';
import { isDefinedAndNotNull } from '@core/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-coap-device-profile-transport-configuration',
@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils';
multi: true
}]
})
export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit {
export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType);
@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
coapDeviceProfileTransportConfigurationFormGroup: FormGroup;
private destroy$ = new Subject();
private requiredValue: boolean;
private transportPayloadTypeConfiguration = this.fb.group({
@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control
})
}
);
this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType')
.valueChanges.subscribe(coapDeviceType => {
this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(coapDeviceType => {
this.updateCoapDeviceTypeBasedControls(coapDeviceType, true);
});
this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel();
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
get coapDeviceTypeDefault(): boolean {
const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value;
return coapDeviceType === CoapTransportDeviceType.DEFAULT;

17
ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts

@ -14,13 +14,15 @@
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models';
import { deepClone } from '@core/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-device-profile-configuration',
@ -32,12 +34,14 @@ import { deepClone } from '@core/utils';
multi: true
}]
})
export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit {
export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
deviceProfileType = DeviceProfileType;
deviceProfileConfigurationFormGroup: FormGroup;
private destroy$ = new Subject();
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor
this.deviceProfileConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required]
});
this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => {
this.deviceProfileConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel();
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {

209
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html

@ -15,112 +15,107 @@
limitations under the License.
-->
<section [formGroup]="serverFormGroup" style="min-width: 400px;">
<div class="mat-padding">
<div fxLayout="column">
<div fxLayout="row" fxLayoutGap="8px">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
[value]="securityMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>
<input matInput type="text" formControlName="host" required
matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('host').hasError('required')">
{{ 'device-profile.lwm2m.server-host' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
<input matInput type="number" formControlName="port" required
matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
{{ 'device-profile.lwm2m.server-port' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
<input matInput type="number" formControlName="serverId" required
matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">
{{ 'device-profile.lwm2m.short-id' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="column">
<div fxLayout="row" fxLayoutGap="10px">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
<input matInput type="number" formControlName="clientHoldOffTime" required
matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label>
<input matInput type="number" formControlName="bootstrapServerAccountTimeout" required
matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-checkbox formControlName="bootstrapServerIs" color="primary">
{{ 'device-profile.lwm2m.bootstrap-server' | translate }}
</mat-checkbox>
</div>
<div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
<textarea matInput
#serverPublicKey
maxlength="{{lenMaxServerPublicKey}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1" required
style="overflow:hidden"
formControlName="serverPublicKey"
matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}"
></textarea>
<mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') &&
(serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
count: 0} }}</strong>
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') ||
serverFormGroup.get('serverPublicKey').hasError('minlength')) &&
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
count: lenMaxServerPublicKey } }}</strong>
</mat-error>
</mat-form-field>
</div>
</div>
<section [formGroup]="serverFormGroup">
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.mode' | translate }}</mat-label>
<mat-select formControlName="securityMode">
<mat-option *ngFor="let securityMode of securityConfigLwM2MTypes"
[value]="securityMode">
{{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-host' | translate }}</mat-label>
<input matInput type="text" formControlName="host" required
matTooltip="{{'device-profile.lwm2m.server-host-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('host').hasError('required')">
{{ 'device-profile.lwm2m.server-host' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.server-port' | translate }}</mat-label>
<input matInput type="number" formControlName="port" required
matTooltip="{{'device-profile.lwm2m.server-port-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('port').hasError('required')">
{{ 'device-profile.lwm2m.server-port' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.short-id' | translate }}</mat-label>
<input matInput type="number" formControlName="serverId" required
matTooltip="{{'device-profile.lwm2m.short-id-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('serverId').hasError('required')">
{{ 'device-profile.lwm2m.short-id' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px" fxLayoutGap.xs="0px">
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}</mat-label>
<input matInput type="number" formControlName="clientHoldOffTime" required
matTooltip="{{'device-profile.lwm2m.client-hold-off-time-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('clientHoldOffTime').hasError('required')">
{{ 'device-profile.lwm2m.client-hold-off-time' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-form-field fxFlex>
<mat-label>{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}</mat-label>
<input matInput type="number" formControlName="bootstrapServerAccountTimeout" required
matTooltip="{{'device-profile.lwm2m.bootstrap-server-account-timeout-tip' | translate}}"
matTooltipPosition="above">
<mat-error *ngIf="serverFormGroup.get('bootstrapServerAccountTimeout').hasError('required')">
{{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
</mat-form-field>
<mat-checkbox fxFlex formControlName="bootstrapServerIs" color="primary">
{{ 'device-profile.lwm2m.bootstrap-server' | translate }}
</mat-checkbox>
<div fxFlex></div>
</div>
<div *ngIf="serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509">
<mat-form-field class="mat-block">
<mat-label>{{ 'device-profile.lwm2m.server-public-key' | translate }}</mat-label>
<textarea matInput
#serverPublicKey
maxlength="{{lenMaxServerPublicKey}}"
cdkTextareaAutosize
cdkAutosizeMinRows="1"
cols="1" required
style="overflow:hidden"
formControlName="serverPublicKey"
matTooltip="{{'device-profile.lwm2m.server-public-key-tip' | translate}}"
></textarea>
<mat-hint align="end">{{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}}</mat-hint>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('required')">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.required' | translate }}</strong>
</mat-error>
<mat-error *ngIf="serverFormGroup.get('serverPublicKey').hasError('pattern') &&
(serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK ||
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.X509)">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
count: 0} }}</strong>
</mat-error>
<mat-error *ngIf="(serverFormGroup.get('serverPublicKey').hasError('maxlength') ||
serverFormGroup.get('serverPublicKey').hasError('minlength')) &&
serverFormGroup.get('securityMode').value === securityConfigLwM2MType.RPK">
{{ 'device-profile.lwm2m.server-public-key' | translate }}
<strong>{{ 'device-profile.lwm2m.pattern_hex_dec' | translate: {
count: lenMaxServerPublicKey } }}</strong>
</mat-error>
</mat-form-field>
</div>
</section>

40
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts

@ -15,7 +15,7 @@
///
import { DeviceProfileTransportConfiguration } from '@shared/models/device.models';
import { Component, forwardRef, Input } from '@angular/core';
import { Component, forwardRef, Input, OnDestroy } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util
import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
import { Direction } from '@shared/models/page/sort-order';
import _ from 'lodash';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-profile-lwm2m-device-transport-configuration',
@ -49,11 +51,12 @@ import _ from 'lodash';
multi: true
}]
})
export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators {
export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators, OnDestroy {
private configurationValue: Lwm2mProfileConfigModels;
private requiredValue: boolean;
private disabled = false;
private destroy$ = new Subject();
bindingModeType = BINDING_MODE;
bindingModeTypes = Object.keys(BINDING_MODE);
@ -86,7 +89,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
lifetime: [null, Validators.required],
defaultMinPeriod: [null, Validators.required],
notifIfDisabled: [true, []],
binding:[],
binding: [],
bootstrapServer: [null, Validators.required],
lwm2mServer: [null, Validators.required],
clientStrategy: [1, []],
@ -96,10 +99,14 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.lwm2mDeviceConfigFormGroup = this.fb.group({
configurationJson: [null, Validators.required]
});
this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => {
this.lwm2mDeviceProfileFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe((value) => {
this.updateDeviceProfileValue(value);
});
this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => {
this.lwm2mDeviceConfigFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel();
});
this.sortFunction = this.sortObjectKeyPathJson;
@ -112,6 +119,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
registerOnTouched(fn: any): void {
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (isDisabled) {
@ -124,11 +136,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}
writeValue(value: Lwm2mProfileConfigModels | null): void {
this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value;
this.lwm2mDeviceConfigFormGroup.patchValue({
configurationJson: this.configurationValue
}, {emitEvent: false});
this.initWriteValue();
if (isDefinedAndNotNull(value)) {
if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) {
this.configurationValue = value;
} else {
this.configurationValue = getDefaultProfileConfig();
}
this.lwm2mDeviceConfigFormGroup.patchValue({
configurationJson: this.configurationValue
}, {emitEvent: false});
this.initWriteValue();
}
}
private initWriteValue = (): void => {
@ -257,7 +275,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
instanceUpdate.id = instanceId;
instanceUpdate.resources.forEach(resource => {
resource.keyName = _.camelCase(resource.name + instanceUpdate.id);
})
});
return instanceUpdate;
}

57
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -15,10 +15,19 @@
///
import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {
ControlValueAccessor,
FormBuilder,
FormGroup,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
ValidationErrors,
Validator,
Validators
} from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { Observable } from 'rxjs';
import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
import { distinctUntilChanged, filter, mergeMap, share, tap } from 'rxjs/operators';
import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { Direction } from '@shared/models/page/sort-order';
@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link';
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => Lwm2mObjectListComponent),
multi: true
}]
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => Lwm2mObjectListComponent),
multi: true
}
]
})
export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validators {
export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validator {
private requiredValue: boolean;
private dirty = false;
private lw2mModels: Observable<Array<ObjectLwM2M>>;
private modelValue: Array<string> = [];
lwm2mListFormGroup: FormGroup;
@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
}
private updateValidators = (): void => {
this.lwm2mListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []);
this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity();
this.lwm2mListFormGroup.get('objectsList').setValidators(this.required ? [Validators.required] : []);
this.lwm2mListFormGroup.get('objectsList').updateValueAndValidity();
}
registerOnChange(fn: any): void {
@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
ngOnInit() {
this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges
.pipe(
distinctUntilChanged(),
tap((value) => {
if (value && typeof value !== 'string') {
this.add(value);
@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
}
}),
filter(searchText => isString(searchText)),
mergeMap(searchText => this.fetchListObjects(searchText))
mergeMap(searchText => this.fetchListObjects(searchText)),
share()
);
}
@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
}
}
validate(): ValidationErrors | null {
return this.lwm2mListFormGroup.valid ? null : {
lwm2mListObj: false
};
}
private add(object: ObjectLwM2M): void {
if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) {
this.modelValue.push(object.keyId);
@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
return object ? object.name : undefined;
}
private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
private fetchListObjects = (searchText: string): Observable<Array<ObjectLwM2M>> => {
this.searchText = searchText;
return this.getLwM2mModelsPage().pipe(
map(objectLwM2Ms => objectLwM2Ms)
);
}
private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {
const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
property: 'id',
direction: Direction.ASC
});
this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
publishReplay(1),
refCount()
);
return this.lw2mModels;
return this.deviceProfileService.getLwm2mObjectsPage(pageLink);
}
onFocus = (): void => {
@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
}
}
private clear = (value: string = ''): void => {
this.objectInput.nativeElement.value = value;
private clear = (): void => {
this.searchText = '';
this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value);
this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null);
setTimeout(() => {
this.objectInput.nativeElement.blur();
this.objectInput.nativeElement.focus();

6
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html

@ -15,7 +15,7 @@
limitations under the License.
-->
<section [formGroup]="resourceFormGroup" class="mat-padding">
<section [formGroup]="resourceFormGroup">
<div fxLayout="row" fxFill formArrayName="resources"
*ngFor="let resourceLwM2M of resourceFormArray.controls; let i = index; trackBy: trackByParams">
<div class="vertical-padding" fxLayout="column" fxFill [formGroupName]="i">
@ -46,14 +46,14 @@
</div>
<div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="attribute" color="warn"
[checked]="updateObserve(i)"
(change)="updateObserve(i)"
matTooltip="{{'device-profile.lwm2m.is-attr-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>
</div>
<div fxFlex="10" fxLayoutAlign="center center">
<mat-checkbox formControlName="telemetry" color="primary"
[checked]="updateObserve(i)"
(change)="updateObserve(i)"
matTooltip="{{'device-profile.lwm2m.is-telemetry-tip' | translate}}"
matTooltipPosition="above">
</mat-checkbox>

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html

@ -16,7 +16,7 @@
-->
<section [formGroup]="observeAttrTelemetryFormGroup">
<mat-accordion multi="true" class="mat-body-1" formArrayName="clientLwM2M">
<mat-accordion multi="true" formArrayName="clientLwM2M">
<mat-expansion-panel
*ngFor="let objectLwM2M of clientLwM2MFormArray.controls; let i = index;"
[formGroupName]="i">

3
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss

@ -24,6 +24,9 @@
}
:host{
section {
padding: 2px;
}
.instance-list {
mat-expansion-panel-header {
color: inherit;

3
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts

@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map<BINDING_MODE, string>(
[BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'],
[BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'],
[BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'],
[BINDING_MODE.T,'T: TCP connection in standard mode'],
[BINDING_MODE.T, 'T: TCP connection in standard mode'],
[BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'],
[BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'],
[BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'],
@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels {
clientLwM2mSettings: ClientLwM2mSettings;
observeAttr: ObservableAttributes;
bootstrap: BootstrapSecurityConfig;
}
export interface ClientLwM2mSettings {

25
ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import {
ControlValueAccessor,
FormBuilder,
@ -39,6 +39,8 @@ import {
transportPayloadTypeTranslationMap
} from '@shared/models/device.models';
import { isDefinedAndNotNull } from '@core/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'tb-mqtt-device-profile-transport-configuration',
@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils';
multi: true
}]
})
export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit {
export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
transportPayloadTypes = Object.keys(TransportPayloadType);
@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
mqttDeviceProfileTransportConfigurationFormGroup: FormGroup;
private destroy$ = new Subject();
private requiredValue: boolean;
get required(): boolean {
@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
})
}, {validator: this.uniqueDeviceTopicValidator}
);
this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType')
.valueChanges.subscribe(payloadType => {
this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType').valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(payloadType => {
this.updateTransportPayloadBasedControls(payloadType, true);
});
this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel();
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
}
private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null {
if (control.value) {
const formValue = control.value as MqttDeviceProfileTransportConfiguration;
if (control.getRawValue()) {
const formValue = control.getRawValue() as MqttDeviceProfileTransportConfiguration;
if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) {
return {unique: true};
}

28
ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts

@ -14,17 +14,19 @@
/// limitations under the License.
///
import {Component, forwardRef, Input, OnInit} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@app/core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@app/core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import {
DeviceProfileTransportConfiguration,
DeviceTransportType,
SnmpDeviceProfileTransportConfiguration
} from '@shared/models/device.models';
import {isDefinedAndNotNull} from "@core/utils";
import { isDefinedAndNotNull } from '@core/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
export interface OidMappingConfiguration {
isAttribute: boolean;
@ -44,8 +46,11 @@ export interface OidMappingConfiguration {
multi: true
}]
})
export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit {
export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy {
snmpDeviceProfileTransportConfigurationFormGroup: FormGroup;
private destroy$ = new Subject();
private requiredValue: boolean;
private configuration = [];
@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control
this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required]
});
this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(() => {
this.updateModel();
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}

2
ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts

@ -32,7 +32,7 @@ import {
})
export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> {
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypes = Object.values(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;

2
ui-ngx/src/app/shared/components/color-input.component.html

@ -20,7 +20,7 @@
<mat-icon *ngIf="icon">{{icon}}</mat-icon>
<span *ngIf="label">{{label}}</span>
</mat-label>
<div matPrefix class="tb-color-preview" (click)="showColorPicker()" style="margin-right: 5px;">
<div matPrefix class="tb-color-preview" (click)="!disabled && showColorPicker()" style="margin-right: 5px;">
<div class="tb-color-result" [ngStyle]="{background: colorFormGroup.get('color').value}"></div>
</div>
<input matInput formControlName="color" (mousedown)="openOnInput && showColorPicker()" [required]="required">

2
ui-ngx/src/app/shared/models/dashboard.models.ts

@ -45,7 +45,6 @@ export interface WidgetLayouts {
export interface GridSettings {
backgroundColor?: string;
color?: string;
columns?: number;
margin?: number;
backgroundSizeMode?: string;
@ -93,6 +92,7 @@ export interface DashboardSettings {
showDashboardExport?: boolean;
showUpdateDashboardImage?: boolean;
toolbarAlwaysOpen?: boolean;
hideToolbar?: boolean;
titleColor?: string;
}

2
ui-ngx/src/app/shared/models/window-message.model.ts

@ -14,7 +14,7 @@
/// limitations under the License.
///
export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage';
export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage' | 'toggleDashboardLayout';
export interface WindowMessage {
type: WindowMessageType;

7
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -721,6 +721,7 @@
"maximum-upload-file-size": "Maximum upload file size: {{ size }}",
"cannot-upload-file": "Cannot upload file",
"settings": "Settings",
"layout-settings": "Layout settings",
"columns-count": "Columns count",
"columns-count-required": "Columns count is required.",
"min-columns-count-message": "Only 10 minimum column count is allowed.",
@ -743,15 +744,19 @@
"mobile-row-height-required": "Mobile row height value is required.",
"min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.",
"max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.",
"title-settings": "Title settings",
"display-title": "Display dashboard title",
"toolbar-always-open": "Keep toolbar opened",
"title-color": "Title color",
"toolbar-settings": "Toolbar settings",
"hide-toolbar": "Hide toolbar",
"toolbar-always-open": "Keep toolbar opened",
"display-dashboards-selection": "Display dashboards selection",
"display-entities-selection": "Display entities selection",
"display-filters": "Display filters",
"display-dashboard-timewindow": "Display timewindow",
"display-dashboard-export": "Display export",
"display-update-dashboard-image": "Display update dashboard image",
"dashboard-logo-settings": "Dashboard logo settings",
"display-dashboard-logo": "Display logo in dashboard fullscreen mode",
"dashboard-logo-image": "Dashboard logo image",
"import": "Import dashboard",

Loading…
Cancel
Save