Browse Source

Merge branch 'vvlladd28-improvement/device-profile/refactoring'

pull/3576/head
Igor Kulikov 6 years ago
parent
commit
93fc099ee3
  1. 16
      ui-ngx/src/app/core/utils.ts
  2. 17
      ui-ngx/src/app/modules/home/components/entity/entity.component.ts
  3. 1
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html
  4. 2
      ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts
  5. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  6. 40
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  7. 46
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss
  8. 80
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  9. 126
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html
  10. 7
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts
  11. 315
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html
  12. 20
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.scss
  13. 51
      ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts
  14. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html
  15. 7
      ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.scss
  16. 95
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html
  17. 14
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss
  18. 4
      ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.scss
  19. 55
      ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html
  20. 110
      ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.ts
  21. 17
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  22. 8
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  23. 6
      ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.html
  24. 6
      ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.html
  25. 52
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html
  26. 13
      ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts
  27. 2
      ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts
  28. 6
      ui-ngx/src/app/shared/models/device.models.ts

16
ui-ngx/src/app/core/utils.ts

@ -387,3 +387,19 @@ export function sortObjectKeys<T>(obj: T): T {
return acc;
}, {} as T);
}
export function deepTrim<T>(obj: T): T {
if (isNumber(obj) || isUndefined(obj) || isString(obj) || obj === null) {
return obj;
}
return Object.keys(obj).reduce((acc, curr) => {
if (isString(obj[curr])) {
acc[curr] = obj[curr].trim();
} else if (isObject(obj[curr])) {
acc[curr] = deepTrim(obj[curr]);
} else {
acc[curr] = obj[curr];
}
return acc;
}, (Array.isArray(obj) ? [] : {}) as T);
}

17
ui-ngx/src/app/modules/home/components/entity/entity.component.ts

@ -23,7 +23,7 @@ import { AppState } from '@core/core.state';
import { EntityAction } from '@home/models/entity/entity-component.models';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { PageLink } from '@shared/models/page/page-link';
import { isObject, isString } from '@core/utils';
import { deepTrim } from '@core/utils';
// @dynamic
@Directive()
@ -115,20 +115,7 @@ export abstract class EntityComponent<T extends BaseData<HasId>,
}
prepareFormValue(formValue: any): any {
return this.deepTrim(formValue);
}
private deepTrim(obj: object): object {
return Object.keys(obj).reduce((acc, curr) => {
if (isString(obj[curr])) {
acc[curr] = obj[curr].trim();
} else if (isObject(obj[curr])) {
acc[curr] = this.deepTrim(obj[curr]);
} else {
acc[curr] = obj[curr];
}
return acc;
}, Array.isArray(obj) ? [] : {});
return deepTrim(formValue);
}
protected setEntitiesTableConfig(entitiesTableConfig: C) {

1
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html

@ -76,7 +76,6 @@
type="button"
matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}"
matTooltipPosition="above"
*ngIf="allow"
(click)="dynamicMode = !dynamicMode">
<mat-icon class="tb-mat-20" [svgIcon]="dynamicMode ? 'mdi:numeric' : 'mdi:variable'"></mat-icon>
</button>

2
ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts

@ -54,7 +54,7 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn
if (allow) {
this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER);
} else {
this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_DEVICE);
this.dynamicValueSourceTypes = [DynamicValueSourceType.CURRENT_DEVICE];
}
}

3
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -90,7 +90,6 @@ import { TenantProfileDialogComponent } from './profile/tenant-profile-dialog.co
import { TenantProfileDataComponent } from './profile/tenant-profile-data.component';
import { DefaultDeviceProfileConfigurationComponent } from './profile/device/default-device-profile-configuration.component';
import { DeviceProfileConfigurationComponent } from './profile/device/device-profile-configuration.component';
import { DeviceProfileDataComponent } from './profile/device-profile-data.component';
import { DeviceProfileComponent } from './profile/device-profile.component';
import { DefaultDeviceProfileTransportConfigurationComponent } from './profile/device/default-device-profile-transport-configuration.component';
import { DeviceProfileTransportConfigurationComponent } from './profile/device/device-profile-transport-configuration.component';
@ -195,7 +194,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
AlarmRuleConditionComponent,
DeviceProfileAlarmComponent,
DeviceProfileAlarmsComponent,
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,
@ -277,7 +275,6 @@ import { DeviceCredentialsComponent } from './device/device-credentials.componen
AlarmRuleConditionComponent,
DeviceProfileAlarmComponent,
DeviceProfileAlarmsComponent,
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent,

40
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -29,7 +29,7 @@
</mat-progress-bar>
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
<div mat-dialog-content>
<mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="selectedIndex = $event.selectedIndex">
<mat-horizontal-stepper [linear]="true" #addDeviceProfileStepper (selectionChange)="changeStep($event)">
<mat-step [stepControl]="deviceProfileDetailsFormGroup">
<form [formGroup]="deviceProfileDetailsFormGroup" style="padding-bottom: 16px;">
<ng-template matStepLabel>{{ 'device-profile.device-profile-details' | translate }}</ng-template>
@ -45,7 +45,7 @@
labelText="device-profile.default-rule-chain"
formControlName="defaultRuleChainId">
</tb-rule-chain-autocomplete>
<mat-form-field class="mat-block">
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let type of deviceProfileTypes" [value]="type">
@ -63,7 +63,7 @@
</fieldset>
</form>
</mat-step>
<mat-step [stepControl]="transportConfigFormGroup">
<mat-step [stepControl]="transportConfigFormGroup" [optional]="true">
<form [formGroup]="transportConfigFormGroup" style="padding-bottom: 16px;">
<ng-template matStepLabel>{{ 'device-profile.transport-configuration' | translate }}</ng-template>
<mat-form-field class="mat-block">
@ -73,6 +73,9 @@
{{deviceTransportTypeTranslations.get(type) | translate}}
</mat-option>
</mat-select>
<mat-hint *ngIf="transportConfigFormGroup.get('transportType').value">
{{deviceTransportTypeHints.get(transportConfigFormGroup.get('transportType').value) | translate}}
</mat-hint>
<mat-error *ngIf="transportConfigFormGroup.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
@ -83,7 +86,7 @@
</tb-device-profile-transport-configuration>
</form>
</mat-step>
<mat-step [stepControl]="alarmRulesFormGroup">
<mat-step [stepControl]="alarmRulesFormGroup" [optional]="true">
<form [formGroup]="alarmRulesFormGroup" style="padding-bottom: 16px;">
<ng-template matStepLabel>{{'device-profile.alarm-rules-with-count' | translate:
{count: alarmRulesFormGroup.get('alarms').value ?
@ -95,19 +98,28 @@
</mat-step>
</mat-horizontal-stepper>
</div>
<div mat-dialog-actions fxLayout="row wrap" fxLayoutAlign="space-between center">
<button mat-button *ngIf="selectedIndex > 0"
[disabled]="(isLoading$ | async)"
(click)="previousStep()">{{ 'action.back' | translate }}</button>
<span *ngIf="selectedIndex <= 0"></span>
<div fxLayout="row wrap" fxLayoutGap="20px">
<div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;">
<div fxFlex fxLayout="row" fxLayoutAlign="end">
<button mat-raised-button
*ngIf="showNext"
[disabled]="(isLoading$ | async)"
(click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button>
</div>
<div fxFlex fxLayout="row">
<button mat-button
color="primary"
[disabled]="(isLoading$ | async)"
(click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-raised-button
[disabled]="(isLoading$ | async) || selectedForm().invalid"
color="primary"
(click)="nextStep()">{{ (selectedIndex === 2 ? 'action.add' : 'action.continue') | translate }}</button>
<span fxFlex></span>
<div fxLayout="row wrap" fxLayoutGap="8px">
<button mat-raised-button *ngIf="selectedIndex > 0"
[disabled]="(isLoading$ | async)"
(click)="previousStep()">{{ 'action.back' | translate }}</button>
<button mat-raised-button
[disabled]="(isLoading$ | async)"
color="primary"
(click)="add()">{{ 'action.add' | translate }}</button>
</div>
</div>
</div>
</div>

46
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss

@ -13,25 +13,51 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.mat-dialog-content {
display: flex;
flex-direction: column;
overflow: hidden;
@import "../../../../../scss/constants";
.mat-stepper-horizontal {
display: flex;
flex-direction: column;
overflow: hidden;
:host-context(.tb-fullscreen-dialog .mat-dialog-container) {
@media #{$mat-lt-sm} {
.mat-dialog-content {
max-height: 75vh;
}
}
}
:host ::ng-deep {
.mat-dialog-content {
display: flex;
flex-direction: column;
height: 100%;
padding: 24px 24px 8px !important;
.mat-stepper-horizontal {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
@media #{$mat-lt-sm} {
.mat-step-label {
white-space: normal;
overflow: visible;
.mat-step-text-label {
overflow: visible;
}
}
}
.mat-horizontal-content-container {
overflow: auto;
height: 350px;
max-height: 100%;
width: 100%;;
overflow-y: auto;
@media #{$mat-gt-sm} {
min-width: 800px;
}
}
.mat-horizontal-stepper-content[aria-expanded=true] {
height: 100%;
form {
height: 100%;
}
}
}
}

80
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -36,13 +36,15 @@ import {
DeviceProfile,
DeviceProfileType,
deviceProfileTypeTranslationMap,
DeviceTransportType,
DeviceTransportType, deviceTransportTypeHintMap,
deviceTransportTypeTranslationMap
} from '@shared/models/device.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
import { EntityType } from '@shared/models/entity-type.models';
import { MatHorizontalStepper } from '@angular/material/stepper';
import { RuleChainId } from '@shared/models/id/rule-chain-id';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
import { deepTrim } from '@core/utils';
export interface AddDeviceProfileDialogData {
deviceProfileName: string;
@ -62,12 +64,16 @@ export class AddDeviceProfileDialogComponent extends
selectedIndex = 0;
showNext = true;
entityType = EntityType;
deviceProfileTypes = Object.keys(DeviceProfileType);
deviceProfileTypeTranslations = deviceProfileTypeTranslationMap;
deviceTransportTypeHints = deviceTransportTypeHintMap;
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
@ -150,25 +156,63 @@ export class AddDeviceProfileDialogComponent extends
}
}
private add(): void {
const deviceProfile: DeviceProfile = {
name: this.deviceProfileDetailsFormGroup.get('name').value,
type: this.deviceProfileDetailsFormGroup.get('type').value,
transportType: this.transportConfigFormGroup.get('transportType').value,
description: this.deviceProfileDetailsFormGroup.get('description').value,
profileData: {
configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
alarms: this.alarmRulesFormGroup.get('alarms').value
add(): void {
if (this.allValid()) {
const deviceProfile: DeviceProfile = {
name: this.deviceProfileDetailsFormGroup.get('name').value,
type: this.deviceProfileDetailsFormGroup.get('type').value,
transportType: this.transportConfigFormGroup.get('transportType').value,
description: this.deviceProfileDetailsFormGroup.get('description').value,
profileData: {
configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT),
transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value,
alarms: this.alarmRulesFormGroup.get('alarms').value
}
};
if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) {
deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value);
}
};
if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) {
deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value);
this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).subscribe(
(savedDeviceProfile) => {
this.dialogRef.close(savedDeviceProfile);
}
);
}
}
getFormLabel(index: number): string {
switch (index) {
case 0:
return 'device-profile.device-profile-details';
case 1:
return 'device-profile.transport-configuration';
case 2:
return 'device-profile.alarm-rules';
}
}
changeStep($event: StepperSelectionEvent): void {
this.selectedIndex = $event.selectedIndex;
if (this.selectedIndex === this.maxStepperIndex) {
this.showNext = false;
} else {
this.showNext = true;
}
this.deviceProfileService.saveDeviceProfile(deviceProfile).subscribe(
(savedDeviceProfile) => {
this.dialogRef.close(savedDeviceProfile);
}
private get maxStepperIndex(): number {
return this.addDeviceProfileStepper?._steps?.length - 1;
}
allValid(): boolean {
return !this.addDeviceProfileStepper.steps.find((item, index) => {
if (item.stepControl.invalid) {
item.interacted = true;
this.addDeviceProfileStepper.selectedIndex = index;
return true;
} else {
return false;
}
);
});
}
}

126
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html

@ -21,74 +21,72 @@
<tb-alarm-rule-condition fxFlex class="row"
formControlName="condition">
</tb-alarm-rule-condition>
<section class="row">
<div formGroupName="spec">
<mat-form-field class="mat-block" hideRequiredMarker>
<mat-label translate>device-profile.condition-type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let alarmConditionType of alarmConditionTypes" [value]="alarmConditionType">
{{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }}
<section formGroupName="spec" class="row">
<mat-form-field class="mat-block" hideRequiredMarker>
<mat-label translate>device-profile.condition-type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let alarmConditionType of alarmConditionTypes" [value]="alarmConditionType">
{{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }}
</mat-option>
</mat-select>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.type').hasError('required')">
{{ 'device-profile.condition-type-required' | translate }}
</mat-error>
</mat-form-field>
<div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.DURATION">
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<input type="number" required
step="1" min="1" max="2147483647" matInput
placeholder="{{ 'device-profile.condition-duration-value' | translate }}"
formControlName="value">
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('required')">
{{ 'device-profile.condition-duration-value-required' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('min')">
{{ 'device-profile.condition-duration-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('max')">
{{ 'device-profile.condition-duration-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('pattern')">
{{ 'device-profile.condition-duration-value-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<mat-select formControlName="unit"
required
placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">
{{ timeUnitTranslations.get(timeUnit) | translate }}
</mat-option>
</mat-select>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.type').hasError('required')">
{{ 'device-profile.condition-type-required' | translate }}
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.unit').hasError('required')">
{{ 'device-profile.condition-duration-time-unit-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.REPEATING">
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<input type="number" required
step="1" min="1" max="2147483647" matInput
placeholder="{{ 'device-profile.condition-repeating-value' | translate }}"
formControlName="count">
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('required')">
{{ 'device-profile.condition-repeating-value-required' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('min')">
{{ 'device-profile.condition-repeating-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('max')">
{{ 'device-profile.condition-repeating-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('pattern')">
{{ 'device-profile.condition-repeating-value-pattern' | translate }}
</mat-error>
</mat-form-field>
<div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.DURATION">
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<input type="number" required
step="1" min="1" max="2147483647" matInput
placeholder="{{ 'device-profile.condition-duration-value' | translate }}"
formControlName="value">
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('required')">
{{ 'device-profile.condition-duration-value-required' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('min')">
{{ 'device-profile.condition-duration-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('max')">
{{ 'device-profile.condition-duration-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('pattern')">
{{ 'device-profile.condition-duration-value-pattern' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<mat-select formControlName="unit"
required
placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}">
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">
{{ timeUnitTranslations.get(timeUnit) | translate }}
</mat-option>
</mat-select>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.unit').hasError('required')">
{{ 'device-profile.condition-duration-time-unit-required' | translate }}
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.REPEATING">
<mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
<mat-label></mat-label>
<input type="number" required
step="1" min="1" max="2147483647" matInput
placeholder="{{ 'device-profile.condition-repeating-value' | translate }}"
formControlName="count">
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('required')">
{{ 'device-profile.condition-repeating-value-required' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('min')">
{{ 'device-profile.condition-repeating-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('max')">
{{ 'device-profile.condition-repeating-value-range' | translate }}
</mat-error>
<mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('pattern')">
{{ 'device-profile.condition-repeating-value-pattern' | translate }}
</mat-error>
</mat-form-field>
</div>
</div>
</section>
</mat-tab>

7
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.ts

@ -29,6 +29,7 @@ import { AlarmConditionType, AlarmConditionTypeTranslationMap, AlarmRule } from
import { MatDialog } from '@angular/material/dialog';
import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { isUndefined } from '@core/utils';
@Component({
selector: 'tb-alarm-rule',
@ -117,10 +118,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
writeValue(value: AlarmRule): void {
this.modelValue = value;
if (this.modelValue?.condition?.spec === null) {
this.modelValue.condition.spec = {
type: AlarmConditionType.SIMPLE
};
if (this.modelValue !== null && isUndefined(this.modelValue?.condition?.spec)) {
this.modelValue = Object.assign(this.modelValue, {condition: {spec: {type: AlarmConditionType.SIMPLE}}});
}
this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false});
this.updateValidators(this.modelValue?.condition?.spec?.type);

315
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.html

@ -35,7 +35,7 @@
</tb-timezone-select>
<section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.SPECIFIC_TIME">
<div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap="16px" style="padding-bottom: 16px;">
<div fxLayout="column" fxLayout.gt-md="row" fxLayoutGap="16px" style="padding-bottom: 16px;">
<div fxLayout="row" fxLayoutGap="16px">
<mat-checkbox [formControl]="weeklyRepeatControl(0)">
{{ 'device-profile.schedule-day.monday' | translate }}
@ -64,158 +64,189 @@
</div>
<div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-time</div>
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px">
<mat-form-field fxFlex>
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker">
</mat-form-field>
<mat-form-field fxFlex>
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker">
</mat-form-field>
<div fxLayout="row" fxLayoutGap="8px" fxFlex.gt-md>
<mat-form-field fxFlex.xs fxFlex.sm="150px" fxFlex.md="150px" fxFlex.gt-md>
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="150px" fxFlex.md="150px" fxFlex.gt-md>
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center" style="margin: auto">
<div style="text-align: center"
[innerHTML]="getSchedulerRangeText(alarmScheduleForm)">
</div>
</div>
</div>
</section>
<section *ngIf="alarmScheduleForm.get('type').value === alarmScheduleType.CUSTOM">
<div class="tb-small" style="margin-bottom: 0.5em" translate>device-profile.schedule-days</div>
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutGap.gt-sm="16px" formArrayName="items">
<div fxLayout="column" fxFlex fxFlex.gt-sm="50">
<div fxLayout="row" fxLayoutGap="8px" formGroupName="0" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 0)">
{{ 'device-profile.schedule-day.monday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker1" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker1">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker1" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker1">
</mat-form-field>
</div>
<div fxLayout="column" formArrayName="items" fxLayoutGap="1em">
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="0" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 0)">
{{ 'device-profile.schedule-day.monday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker1" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker1">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker1" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker1 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker1">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" formGroupName="1" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 1)">
{{ 'device-profile.schedule-day.tuesday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker2" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker2">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker2" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker2">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(0))">
</div>
<div fxLayout="row" fxLayoutGap="8px" formGroupName="2" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 2)">
{{ 'device-profile.schedule-day.wednesday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker3" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker3">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker3" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker3">
</mat-form-field>
</div>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="1" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 1)">
{{ 'device-profile.schedule-day.tuesday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker2" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker2">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker2" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker2 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker2">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" formGroupName="3" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 3)">
{{ 'device-profile.schedule-day.thursday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker4" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker4">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker4" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker4">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(1))">
</div>
</div>
<div fxLayout="column" fxFlex fxFlex.gt-sm="50">
<div fxLayout="row" fxLayoutGap="8px" formGroupName="4" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 4)">
{{ 'device-profile.schedule-day.friday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker5" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker5">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker5" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker5">
</mat-form-field>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="2" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 2)">
{{ 'device-profile.schedule-day.wednesday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker3" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker3">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker3" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker3 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker3">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(2))">
</div>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="3" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 3)">
{{ 'device-profile.schedule-day.thursday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker4" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker4">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker4" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker4 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker4">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" formGroupName="5" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 5)">
{{ 'device-profile.schedule-day.saturday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker6" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker6">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker6" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker6">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(3))">
</div>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="4" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 4)">
{{ 'device-profile.schedule-day.friday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker5" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker5">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker5" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker5 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker5">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(4))">
</div>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="5" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 5)">
{{ 'device-profile.schedule-day.saturday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker6" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker6">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker6" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker6 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker6">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(5))">
</div>
</div>
<div fxLayout.xs="column" fxLayout="row" fxLayoutGap="8px" formGroupName="6" fxLayoutAlign="start center" fxLayoutAlign.xs="center start">
<mat-checkbox formControlName="enabled" fxFlex="17" (change)="changeCustomScheduler($event, 6)">
{{ 'device-profile.schedule-day.sunday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker7" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker7">
</mat-form-field>
<mat-form-field fxFlex.xs fxFlex.sm="100px" fxFlex.md="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker7" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker7">
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap="8px" formGroupName="6" fxLayoutAlign="start center">
<mat-checkbox formControlName="enabled" fxFlex="40" (change)="changeCustomScheduler($event, 6)">
{{ 'device-profile.schedule-day.sunday' | translate }}
</mat-checkbox>
<div fxLayout="row" fxLayoutGap="8px" fxFlex>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-from</mat-label>
<mat-datetimepicker-toggle [for]="startTimePicker7" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #startTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="startsOn" [matDatetimepicker]="startTimePicker7">
</mat-form-field>
<mat-form-field fxFlex="100px">
<mat-label translate>device-profile.schedule-time-to</mat-label>
<mat-datetimepicker-toggle [for]="endTimePicker7" matPrefix></mat-datetimepicker-toggle>
<mat-datetimepicker #endTimePicker7 type="time" openOnFocus="true"></mat-datetimepicker>
<input required matInput formControlName="endsOn" [matDatetimepicker]="endTimePicker7">
</mat-form-field>
</div>
<div fxFlex fxLayoutAlign="center center"
style="text-align: center"
[innerHTML]="getSchedulerRangeText(itemsSchedulerForm.at(6))">
</div>
</div>
</div>

20
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.scss

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 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.
*/
::ng-deep {
.nowrap {
white-space: nowrap;
}
}

51
ui-ngx/src/app/modules/home/components/profile/alarm/alarm-schedule.component.ts

@ -16,6 +16,7 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import {
AbstractControl,
ControlValueAccessor,
FormArray,
FormBuilder,
@ -35,6 +36,7 @@ import { MatCheckboxChange } from '@angular/material/checkbox';
@Component({
selector: 'tb-alarm-schedule',
templateUrl: './alarm-schedule.component.html',
styleUrls: ['./alarm-schedule.component.scss'],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AlarmScheduleComponent),
@ -79,7 +81,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)))
});
this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => {
this.alarmScheduleForm.reset({type, items: this.defaultItems}, {emitEvent: false});
this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: this.defaultTimezone}, {emitEvent: false});
this.updateValidators(type, true);
this.alarmScheduleForm.updateValueAndValidity();
});
@ -131,13 +133,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
this.modelValue.items
.sort((a, b) => a.dayOfWeek - b.dayOfWeek)
.forEach((item, index) => {
if (item.enabled) {
this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false});
this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent: false});
} else {
this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false});
this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent: false});
}
this.disabledSelectedTime(item.enabled, index);
alarmDays.push({
enabled: item.enabled,
startsOn: this.timestampToTime(item.startsOn),
@ -206,15 +202,15 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
.filter(day => !!day);
}
if (isDefined(value.startsOn) && value.startsOn !== 0) {
value.startsOn = this.timeToTimestamp(value.startsOn);
value.startsOn = this.timeToTimestampUTC(value.startsOn);
}
if (isDefined(value.endsOn) && value.endsOn !== 0) {
value.endsOn = this.timeToTimestamp(value.endsOn);
value.endsOn = this.timeToTimestampUTC(value.endsOn);
}
if (isDefined(value.items)){
value.items = this.alarmScheduleForm.getRawValue().items;
value.items = value.items.map((item) => {
return { ...item, startsOn: this.timeToTimestamp(item.startsOn), endsOn: this.timeToTimestamp(item.endsOn)};
return { ...item, startsOn: this.timeToTimestampUTC(item.startsOn), endsOn: this.timeToTimestampUTC(item.endsOn)};
});
}
this.modelValue = value;
@ -222,7 +218,7 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
}
}
private timeToTimestamp(date: Date | number): number {
private timeToTimestampUTC(date: Date | number): number {
if (typeof date === 'number' || date === null) {
return 0;
}
@ -244,16 +240,39 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator,
changeCustomScheduler($event: MatCheckboxChange, index: number) {
const value = $event.checked;
if (value) {
this.disabledSelectedTime(value, index, true);
}
private disabledSelectedTime(enable: boolean, index: number, emitEvent = false) {
if (enable) {
this.itemsSchedulerForm.at(index).get('startsOn').enable({emitEvent: false});
this.itemsSchedulerForm.at(index).get('endsOn').enable();
this.itemsSchedulerForm.at(index).get('endsOn').enable({emitEvent});
} else {
this.itemsSchedulerForm.at(index).get('startsOn').disable({emitEvent: false});
this.itemsSchedulerForm.at(index).get('endsOn').disable();
this.itemsSchedulerForm.at(index).get('endsOn').disable({emitEvent});
}
}
private timeToMoment(date: Date | number): _moment.Moment {
if (typeof date === 'number' || date === null) {
return _moment([1970, 0, 1, 0, 0, 0, 0]);
}
return _moment([1970, 0, 1, date.getHours(), date.getMinutes(), 0, 0]);
}
getSchedulerRangeText(control: FormGroup | AbstractControl): string {
const start = this.timeToMoment(control.get('startsOn').value);
const end = this.timeToMoment(control.get('endsOn').value);
if (start < end) {
return `<span><span class="nowrap">${start.format('hh:mm A')}</span> – <span class="nowrap">${end.format('hh:mm A')}</span></span>`;
} else if (start.valueOf() === 0 && end.valueOf() === 0 || start.isSame(_moment([1970, 0])) && end.isSame(_moment([1970, 0]))) {
return '<span><span class="nowrap">12:00 AM</span> – <span class="nowrap">12:00 PM</span></span>';
}
return `<span><span class="nowrap">12:00 AM</span> – <span class="nowrap">${end.format('hh:mm A')}</span>` +
` and <span class="nowrap">${start.format('hh:mm A')}</span> – <span class="nowrap">12:00 PM</span></span>`;
}
private get itemsSchedulerForm(): FormArray {
get itemsSchedulerForm(): FormArray {
return this.alarmScheduleForm.get('items') as FormArray;
}
}

4
ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html

@ -48,7 +48,7 @@
</button>
</div>
<div *ngIf="!createAlarmRulesFormArray().controls.length">
<span translate fxLayoutAlign="center center"
<span translate fxLayoutAlign="center center" style="margin: 16px 0"
class="tb-prompt">device-profile.no-create-alarm-rules</span>
</div>
<div fxLayout="row" *ngIf="!disabled">
@ -57,7 +57,7 @@
(click)="addCreateAlarmRule()"
matTooltip="{{ 'device-profile.add-create-alarm-rule' | translate }}"
matTooltipPosition="above">
<mat-icon>add_circle_outline</mat-icon>
<mat-icon class="button-icon">add_circle_outline</mat-icon>
{{ 'device-profile.add-create-alarm-rule' | translate }}
</button>
</div>

7
ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.scss

@ -16,7 +16,7 @@
:host {
.create-alarm-rule {
border: 1px groove rgba(0, 0, 0, .25);
border: 2px groove rgba(0, 0, 0, .45);
border-radius: 4px;
padding: 8px;
}
@ -28,4 +28,9 @@
width: 160px;
}
}
.button-icon{
font-size: 20px;
width: 20px;
height: 20px;
}
}

95
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html

@ -18,24 +18,11 @@
<mat-expansion-panel class="device-profile-alarm" fxFlex [formGroup]="alarmFormGroup" [(expanded)]="expanded">
<mat-expansion-panel-header>
<div fxFlex fxLayout="row" fxLayoutAlign="start center">
<mat-panel-title [fxShow]="!expanded">
<mat-panel-title>
<div fxLayout="row" fxFlex fxLayoutAlign="start center">
{{ alarmFormGroup.get('alarmType').value }}
</div>
</mat-panel-title>
<mat-form-field floatLabel="always"
style="width: 600px;"
[fxShow]="expanded"
(keydown)="!disabled ? $event.stopPropagation() : null;"
(click)="!disabled ? $event.stopPropagation() : null;">
<mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
<input required matInput formControlName="alarmType" placeholder="Enter alarm type">
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
{{ 'device-profile.alarm-type-required' | translate }}
</mat-error>
<mat-hint *ngIf="!disabled"
innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
</mat-form-field>
<span fxFlex></span>
<button *ngIf="!disabled" mat-icon-button style="min-width: 40px;"
type="button"
@ -46,6 +33,50 @@
</button>
</div>
</mat-expansion-panel-header>
<div fxLayout="column" fxLayoutGap="0.5em">
<mat-divider></mat-divider>
<mat-form-field fxFlex floatLabel="always">
<mat-label>{{'device-profile.alarm-type' | translate}}</mat-label>
<input required matInput formControlName="alarmType" placeholder="Enter alarm type">
<mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')">
{{ 'device-profile.alarm-type-required' | translate }}
</mat-error>
<mat-hint *ngIf="!disabled"
innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint>
</mat-form-field>
</div>
<mat-expansion-panel class="advanced-settings" [expanded]="false">
<mat-expansion-panel-header>
<mat-panel-title>
<div fxFlex fxLayout="row" fxLayoutAlign="end center">
<div class="tb-small" translate>device-profile.advanced-settings</div>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;">
{{ 'device-profile.propagate-alarm' | translate }}
</mat-checkbox>
<section *ngIf="alarmFormGroup.get('propagate').value === true">
<mat-form-field floatLabel="always" class="mat-block">
<mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label>
<mat-chip-list #relationTypesChipList [disabled]="disabled">
<mat-chip
*ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;"
(removed)="removeRelationType(key)">
{{key}}
<mat-icon matChipRemove>close</mat-icon>
</mat-chip>
<input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}"
style="max-width: 200px;"
[matChipInputFor]="relationTypesChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addRelationType($event)"
[matChipInputAddOnBlur]="true">
</mat-chip-list>
<mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint>
</mat-form-field>
</section>
</mat-expansion-panel>
<div fxFlex fxLayout="column">
<div translate class="tb-small" style="padding-bottom: 8px;">device-profile.create-alarm-rules</div>
<tb-create-alarm-rules formControlName="createRules" style="padding-bottom: 16px;">
@ -68,7 +99,7 @@
</button>
</div>
<div *ngIf="!alarmFormGroup.get('clearRule').value">
<span translate fxLayoutAlign="center center"
<span translate fxLayoutAlign="center center" style="margin: 16px 0"
class="tb-prompt">device-profile.no-clear-alarm-rule</span>
</div>
<div fxLayout="row" *ngIf="!disabled"
@ -78,41 +109,9 @@
(click)="addClearAlarmRule()"
matTooltip="{{ 'device-profile.add-clear-alarm-rule' | translate }}"
matTooltipPosition="above">
<mat-icon>add_circle_outline</mat-icon>
<mat-icon class="button-icon">add_circle_outline</mat-icon>
{{ 'device-profile.add-clear-alarm-rule' | translate }}
</button>
</div>
</div>
<mat-expansion-panel class="advanced-settings" [expanded]="false">
<mat-expansion-panel-header>
<mat-panel-title>
<div fxFlex fxLayout="row" fxLayoutAlign="end center">
<div class="tb-small" translate>device-profile.advanced-settings</div>
</div>
</mat-panel-title>
</mat-expansion-panel-header>
<mat-checkbox formControlName="propagate" style="display: block; padding-bottom: 16px;">
{{ 'device-profile.propagate-alarm' | translate }}
</mat-checkbox>
<section *ngIf="alarmFormGroup.get('propagate').value === true">
<mat-form-field floatLabel="always" class="mat-block">
<mat-label translate>device-profile.alarm-rule-relation-types-list</mat-label>
<mat-chip-list #relationTypesChipList [disabled]="disabled">
<mat-chip
*ngFor="let key of alarmFormGroup.get('propagateRelationTypes').value;"
(removed)="removeRelationType(key)">
{{key}}
<mat-icon matChipRemove>close</mat-icon>
</mat-chip>
<input matInput type="text" placeholder="{{'device-profile.alarm-rule-relation-types-list' | translate}}"
style="max-width: 200px;"
[matChipInputFor]="relationTypesChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="addRelationType($event)"
[matChipInputAddOnBlur]="true">
</mat-chip-list>
<mat-hint innerHTML="{{ 'device-profile.alarm-rule-relation-types-list-hint' | translate }}"></mat-hint>
</mat-form-field>
</section>
</mat-expansion-panel>
</mat-expansion-panel>

14
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.scss

@ -17,7 +17,7 @@
:host {
display: block;
.clear-alarm-rule {
border: 1px groove rgba(0, 0, 0, .25);
border: 2px groove rgba(0, 0, 0, .45);
border-radius: 4px;
padding: 8px;
}
@ -28,13 +28,16 @@
.mat-expansion-panel-header {
padding: 0 24px 0 8px;
&.mat-expanded {
height: 80px;
height: 48px;
}
}
}
&.advanced-settings {
border: none;
padding: 0;
.mat-expansion-panel-header {
padding: 0 8px;
}
}
}
}
@ -43,7 +46,7 @@
.mat-expansion-panel {
&.device-profile-alarm {
.mat-expansion-panel-body {
padding: 0 8px;
padding: 0 8px 8px;
}
}
&.advanced-settings {
@ -51,5 +54,10 @@
padding: 0;
}
}
.button-icon{
font-size: 20px;
width: 20px;
height: 20px;
}
}
}

4
ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.scss

@ -25,4 +25,8 @@
}
}
}
.tb-prompt{
margin: 30px 0;
}
}

55
ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html

@ -1,55 +0,0 @@
<!--
Copyright © 2016-2020 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 [formGroup]="deviceProfileDataFormGroup" style="padding-bottom: 16px;">
<mat-accordion multi="true">
<mat-expansion-panel *ngIf="displayProfileConfiguration" [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>device-profile.profile-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-configuration
formControlName="configuration"
required>
</tb-device-profile-configuration>
</mat-expansion-panel>
<mat-expansion-panel *ngIf="displayTransportConfiguration" [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div translate>device-profile.transport-configuration</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
required>
</tb-device-profile-transport-configuration>
</mat-expansion-panel>
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header>
<mat-panel-title>
<div>{{'device-profile.alarm-rules-with-count' | translate:
{count: deviceProfileDataFormGroup.get('alarms').value ?
deviceProfileDataFormGroup.get('alarms').value.length : 0} }}</div>
</mat-panel-title>
</mat-expansion-panel-header>
<tb-device-profile-alarms
formControlName="alarms">
</tb-device-profile-alarms>
</mat-expansion-panel>
</mat-accordion>
</div>

110
ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.ts

@ -1,110 +0,0 @@
///
/// Copyright © 2016-2020 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 { 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 {
DeviceProfileData,
DeviceProfileType,
deviceProfileTypeConfigurationInfoMap,
DeviceTransportType, deviceTransportTypeConfigurationInfoMap
} from '@shared/models/device.models';
@Component({
selector: 'tb-device-profile-data',
templateUrl: './device-profile-data.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DeviceProfileDataComponent),
multi: true
}]
})
export class DeviceProfileDataComponent implements ControlValueAccessor, OnInit {
deviceProfileDataFormGroup: FormGroup;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
displayProfileConfiguration: boolean;
displayTransportConfiguration: boolean;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.deviceProfileDataFormGroup = this.fb.group({
configuration: [null, Validators.required],
transportConfiguration: [null, Validators.required],
alarms: [null]
});
this.deviceProfileDataFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.deviceProfileDataFormGroup.disable({emitEvent: false});
} else {
this.deviceProfileDataFormGroup.enable({emitEvent: false});
}
}
writeValue(value: DeviceProfileData | null): void {
const deviceProfileType = value?.configuration?.type;
this.displayProfileConfiguration = deviceProfileType &&
deviceProfileTypeConfigurationInfoMap.get(deviceProfileType).hasProfileConfiguration;
const deviceTransportType = value?.transportConfiguration?.type;
this.displayTransportConfiguration = deviceTransportType &&
deviceTransportTypeConfigurationInfoMap.get(deviceTransportType).hasProfileConfiguration;
this.deviceProfileDataFormGroup.patchValue({configuration: value?.configuration}, {emitEvent: false});
this.deviceProfileDataFormGroup.patchValue({transportConfiguration: value?.transportConfiguration}, {emitEvent: false});
this.deviceProfileDataFormGroup.patchValue({alarms: value?.alarms}, {emitEvent: false});
}
private updateModel() {
let deviceProfileData: DeviceProfileData = null;
if (this.deviceProfileDataFormGroup.valid) {
deviceProfileData = this.deviceProfileDataFormGroup.getRawValue();
}
this.propagateChange(deviceProfileData);
}
}

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

@ -53,7 +53,7 @@
labelText="device-profile.default-rule-chain"
formControlName="defaultRuleChainId">
</tb-rule-chain-autocomplete>
<mat-form-field class="mat-block">
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>
<mat-option *ngFor="let type of deviceProfileTypes" [value]="type">
@ -64,21 +64,6 @@
{{ 'device-profile.type-required' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
{{deviceTransportTypeTranslations.get(type) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="entityForm.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
</mat-form-field>
<tb-device-profile-data
formControlName="profileData"
required>
</tb-device-profile-data>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>

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

@ -77,7 +77,11 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
name: [entity ? entity.name : '', [Validators.required]],
type: [entity ? entity.type : null, [Validators.required]],
transportType: [entity ? entity.transportType : null, [Validators.required]],
profileData: [entity && !this.isAdd ? entity.profileData : {}, []],
profileData: this.fb.group({
configuration: [entity && !this.isAdd ? entity.profileData?.configuration : {}, Validators.required],
transportConfiguration: [entity && !this.isAdd ? entity.profileData?.transportConfiguration : {}, Validators.required],
alarms: [entity && !this.isAdd ? entity.profileData?.alarms : []]
}),
defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []],
description: [entity ? entity.description : '', []],
}
@ -138,7 +142,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
if (formValue.defaultRuleChainId) {
formValue.defaultRuleChainId = new RuleChainId(formValue.defaultRuleChainId);
}
return formValue;
return super.prepareFormValue(formValue);
}
onDeviceProfileIdCopied(event) {

6
ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-configuration.component.html

@ -16,9 +16,5 @@
-->
<form [formGroup]="defaultDeviceProfileConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
[required]="required"
label="{{ 'device-profile.type-default' | translate }}"
formControlName="configuration">
</tb-json-object-edit>
</form>

6
ui-ngx/src/app/modules/home/components/profile/device/default-device-profile-transport-configuration.component.html

@ -16,9 +16,5 @@
-->
<form [formGroup]="defaultDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
[required]="required"
label="{{ 'device-profile.transport-type-default' | translate }}"
formControlName="configuration">
</tb-json-object-edit>
</form>

52
ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html

@ -16,6 +16,52 @@
-->
<mat-tab *ngIf="entity"
label="{{ 'device-profile.transport-configuration' | translate }}" #transportType="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<mat-form-field class="mat-block">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>
<mat-option *ngFor="let type of deviceTransportTypes" [value]="type">
{{deviceTransportTypeTranslations.get(type) | translate}}
</mat-option>
</mat-select>
<mat-hint *ngIf="detailsForm.get('transportType').value">
{{deviceTransportTypeHints.get(detailsForm.get('transportType').value) | translate}}
</mat-hint>
<mat-error *ngIf="detailsForm.get('transportType').hasError('required')">
{{ 'device-profile.transport-type-required' | translate }}
</mat-error>
</mat-form-field>
<div formGroupName="profileData">
<tb-device-profile-transport-configuration
formControlName="transportConfiguration"
required>
</tb-device-profile-transport-configuration>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="entity"
label="{{'device-profile.alarm-rules' | translate:
{count: entity.profileData.alarms && entity.profileData.alarms.length ?
entity.profileData.alarms.length : 0} }}" #alarmRules="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<div formGroupName="profileData">
<tb-device-profile-alarms formControlName="alarms"></tb-device-profile-alarms>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="false"
label="{{'device-profile.profile-configuration' | translate }}" #deviceProfile="matTab">
<div class="mat-padding" [formGroup]="detailsForm">
<div formGroupName="profileData">
<tb-device-profile-configuration
formControlName="configuration"
required>
</tb-device-profile-configuration>
</div>
</div>
</mat-tab>
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab">
<tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"
[active]="attributesTab.isActive"
@ -23,7 +69,7 @@
[entityName]="entity.name">
</tb-attribute-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab">
<tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY"
disableAttributeScopeSelection
@ -32,11 +78,11 @@
[entityName]="entity.name">
</tb-attribute-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'alarm.alarms' | translate }}" #alarmsTab="matTab">
<tb-alarm-table [active]="alarmsTab.isActive" [entityId]="entity.id"></tb-alarm-table>
</mat-tab>
<mat-tab *ngIf="entity"
<mat-tab *ngIf="entity && !isEdit"
label="{{ 'tenant.events' | translate }}" #eventsTab="matTab">
<tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid"
[entityId]="entity.id"></tb-event-table>

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

@ -18,7 +18,12 @@ import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityTabsComponent } from '../../components/entity/entity-tabs.component';
import { DeviceProfile } from '@shared/models/device.models';
import {
DeviceProfile,
DeviceTransportType,
deviceTransportTypeHintMap,
deviceTransportTypeTranslationMap
} from '@shared/models/device.models';
@Component({
selector: 'tb-device-profile-tabs',
@ -27,6 +32,12 @@ import { DeviceProfile } from '@shared/models/device.models';
})
export class DeviceProfileTabsComponent extends EntityTabsComponent<DeviceProfile> {
deviceTransportTypes = Object.keys(DeviceTransportType);
deviceTransportTypeTranslations = deviceTransportTypeTranslationMap;
deviceTransportTypeHints = deviceTransportTypeHintMap;
constructor(protected store: Store<AppState>) {
super(store);
}

2
ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts

@ -59,6 +59,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
this.config.entityTranslations = entityTypeTranslations.get(EntityType.DEVICE_PROFILE);
this.config.entityResources = entityTypeResources.get(EntityType.DEVICE_PROFILE);
this.config.hideDetailsTabsOnEdit = false;
this.config.addDialogStyle = {width: '1000px'};
this.config.columns.push(

6
ui-ngx/src/app/shared/models/device.models.ts

@ -33,7 +33,7 @@ export enum DeviceProfileType {
export enum DeviceTransportType {
DEFAULT = 'DEFAULT',
MQTT = 'MQTT',
// LWM2M = 'LWM2M'
// LWM2M = 'LWM2M'
}
export enum MqttTransportPayloadType {
@ -68,7 +68,7 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st
[
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'],
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m']
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m']
]
);
@ -76,7 +76,7 @@ export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>(
[
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'],
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint']
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint']
]
);

Loading…
Cancel
Save