Commit 77d2c786afa1cd7386775a84e9b58adaecaf8da6

Authored by Vladyslav_Prykhodko
1 parent 1278339e

UI: Added device profile alarm conditional type

@@ -15,11 +15,13 @@ @@ -15,11 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 import lombok.Data; 19 import lombok.Data;
19 20
20 import java.util.concurrent.TimeUnit; 21 import java.util.concurrent.TimeUnit;
21 22
22 @Data 23 @Data
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
23 public class DurationAlarmConditionSpec implements AlarmConditionSpec { 25 public class DurationAlarmConditionSpec implements AlarmConditionSpec {
24 26
25 private TimeUnit unit; 27 private TimeUnit unit;
@@ -15,11 +15,13 @@ @@ -15,11 +15,13 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 import lombok.Data; 19 import lombok.Data;
19 20
20 import java.util.concurrent.TimeUnit; 21 import java.util.concurrent.TimeUnit;
21 22
22 @Data 23 @Data
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
23 public class RepeatingAlarmConditionSpec implements AlarmConditionSpec { 25 public class RepeatingAlarmConditionSpec implements AlarmConditionSpec {
24 26
25 private int count; 27 private int count;
@@ -15,9 +15,11 @@ @@ -15,9 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 import lombok.Data; 19 import lombok.Data;
19 20
20 @Data 21 @Data
  22 +@JsonIgnoreProperties(ignoreUnknown = true)
21 public class SimpleAlarmConditionSpec implements AlarmConditionSpec { 23 public class SimpleAlarmConditionSpec implements AlarmConditionSpec {
22 @Override 24 @Override
23 public AlarmConditionSpecType getType() { 25 public AlarmConditionSpecType getType() {
@@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
17 --> 17 -->
18 <div fxLayout="column" fxFlex> 18 <div fxLayout="column" fxFlex>
19 <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;"> 19 <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;">
20 - <div class="tb-small" translate>device-profile.alarm-rule-condition</div>  
21 <span fxFlex></span> 20 <span fxFlex></span>
22 <a mat-button color="primary" 21 <a mat-button color="primary"
23 type="button" 22 type="button"
@@ -16,70 +16,90 @@ @@ -16,70 +16,90 @@
16 16
17 --> 17 -->
18 <div fxLayout="column" [formGroup]="alarmRuleFormGroup"> 18 <div fxLayout="column" [formGroup]="alarmRuleFormGroup">
19 - <div formGroupName="condition" fxLayout="row" fxLayoutGap="8px" fxFlex>  
20 - <tb-alarm-rule-condition fxFlex  
21 - formControlName="condition">  
22 - </tb-alarm-rule-condition>  
23 - <div fxLayout="column">  
24 - <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;">  
25 - <div class="tb-small" translate>device-profile.condition-duration</div>  
26 - <span fxFlex></span>  
27 - <mat-slide-toggle [disabled]="disabled"  
28 - color="primary"  
29 - [ngModelOptions]="{standalone: true}"  
30 - (ngModelChange)="enableDurationChanged($event)"  
31 - [ngModel]="enableDuration">  
32 - </mat-slide-toggle>  
33 - </div>  
34 - <div class="tb-condition-duration" fxFlex fxLayout="row" fxLayoutGap="8px">  
35 - <span style="min-width: 250px;" *ngIf="!enableDuration"></span>  
36 - <div style="min-width: 250px;" fxLayout="row" fxLayoutGap="8px" *ngIf="enableDuration">  
37 - <mat-form-field class="mat-block duration-value-field" hideRequiredMarker floatLabel="always">  
38 - <mat-label></mat-label>  
39 - <input type="number"  
40 - required  
41 - step="1"  
42 - min="1" max="2147483647" matInput  
43 - placeholder="{{ 'device-profile.condition-duration-value' | translate }}"  
44 - formControlName="durationValue">  
45 - <mat-error *ngIf="alarmRuleFormGroup.get('condition').get('durationValue').hasError('required')">  
46 - {{ 'device-profile.condition-duration-value-required' | translate }}  
47 - </mat-error>  
48 - <mat-error *ngIf="alarmRuleFormGroup.get('condition').get('durationValue').hasError('min')">  
49 - {{ 'device-profile.condition-duration-value-range' | translate }}  
50 - </mat-error>  
51 - <mat-error *ngIf="alarmRuleFormGroup.get('condition').get('durationValue').hasError('max')">  
52 - {{ 'device-profile.condition-duration-value-range' | translate }}  
53 - </mat-error>  
54 - </mat-form-field>  
55 - <mat-form-field class="mat-block duration-unit-field" hideRequiredMarker floatLabel="always">  
56 - <mat-label></mat-label>  
57 - <mat-select formControlName="durationUnit"  
58 - required  
59 - placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}">  
60 - <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">  
61 - {{ timeUnitTranslations.get(timeUnit) | translate }} 19 + <mat-tab-group>
  20 + <mat-tab label="{{ 'device-profile.condition' | translate }}" formGroupName="condition">
  21 + <tb-alarm-rule-condition fxFlex class="row"
  22 + formControlName="condition">
  23 + </tb-alarm-rule-condition>
  24 + <section class="row">
  25 + <div formGroupName="spec">
  26 + <mat-form-field class="mat-block" hideRequiredMarker>
  27 + <mat-label translate>device-profile.condition-type</mat-label>
  28 + <mat-select formControlName="type" required>
  29 + <mat-option *ngFor="let alarmConditionType of alarmConditionTypes" [value]="alarmConditionType">
  30 + {{ alarmConditionTypeTranslation.get(alarmConditionType) | translate }}
62 </mat-option> 31 </mat-option>
63 </mat-select> 32 </mat-select>
64 - <mat-error *ngIf="alarmRuleFormGroup.get('condition').get('durationUnit').hasError('required')">  
65 - {{ 'device-profile.condition-duration-time-unit-required' | translate }} 33 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.type').hasError('required')">
  34 + {{ 'device-profile.condition-type-required' | translate }}
66 </mat-error> 35 </mat-error>
67 </mat-form-field> 36 </mat-form-field>
  37 + <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.DURATION">
  38 + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
  39 + <mat-label></mat-label>
  40 + <input type="number" required
  41 + step="1" min="1" max="2147483647" matInput
  42 + placeholder="{{ 'device-profile.condition-duration-value' | translate }}"
  43 + formControlName="value">
  44 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('required')">
  45 + {{ 'device-profile.condition-duration-value-required' | translate }}
  46 + </mat-error>
  47 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('min')">
  48 + {{ 'device-profile.condition-duration-value-range' | translate }}
  49 + </mat-error>
  50 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('max')">
  51 + {{ 'device-profile.condition-duration-value-range' | translate }}
  52 + </mat-error>
  53 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.value').hasError('pattern')">
  54 + {{ 'device-profile.condition-duration-value-pattern' | translate }}
  55 + </mat-error>
  56 + </mat-form-field>
  57 + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
  58 + <mat-label></mat-label>
  59 + <mat-select formControlName="unit"
  60 + required
  61 + placeholder="{{ 'device-profile.condition-duration-time-unit' | translate }}">
  62 + <mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit">
  63 + {{ timeUnitTranslations.get(timeUnit) | translate }}
  64 + </mat-option>
  65 + </mat-select>
  66 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.unit').hasError('required')">
  67 + {{ 'device-profile.condition-duration-time-unit-required' | translate }}
  68 + </mat-error>
  69 + </mat-form-field>
  70 + </div>
  71 + <div fxLayout="row" fxLayoutGap="8px" *ngIf="alarmRuleFormGroup.get('condition.spec.type').value == AlarmConditionType.REPEATING">
  72 + <mat-form-field class="mat-block" hideRequiredMarker fxFlex floatLabel="always">
  73 + <mat-label></mat-label>
  74 + <input type="number" required
  75 + step="1" min="1" max="2147483647" matInput
  76 + placeholder="{{ 'device-profile.condition-repeating-value' | translate }}"
  77 + formControlName="count">
  78 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('required')">
  79 + {{ 'device-profile.condition-repeating-value-required' | translate }}
  80 + </mat-error>
  81 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('min')">
  82 + {{ 'device-profile.condition-repeating-value-range' | translate }}
  83 + </mat-error>
  84 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('max')">
  85 + {{ 'device-profile.condition-repeating-value-range' | translate }}
  86 + </mat-error>
  87 + <mat-error *ngIf="alarmRuleFormGroup.get('condition.spec.count').hasError('pattern')">
  88 + {{ 'device-profile.condition-repeating-value-pattern' | translate }}
  89 + </mat-error>
  90 + </mat-form-field>
  91 + </div>
68 </div> 92 </div>
69 - </div>  
70 - </div>  
71 - </div>  
72 - <mat-expansion-panel class="advanced-settings" [expanded]="false">  
73 - <mat-expansion-panel-header>  
74 - <mat-panel-title>  
75 - <div fxFlex fxLayout="row" fxLayoutAlign="end center">  
76 - <div class="tb-small" translate>device-profile.alarm-rule-details</div>  
77 - </div>  
78 - </mat-panel-title>  
79 - </mat-expansion-panel-header>  
80 - <mat-form-field class="mat-block">  
81 - <mat-label translate>device-profile.alarm-details</mat-label>  
82 - <textarea matInput formControlName="alarmDetails" rows="5"></textarea>  
83 - </mat-form-field>  
84 - </mat-expansion-panel> 93 + </section>
  94 + </mat-tab>
  95 + <mat-tab label="{{ 'device-profile.schedule' | translate }}">
  96 + <div class="row">{{ 'device-profile.schedule' | translate }}</div>
  97 + </mat-tab>
  98 + <mat-tab label="{{ 'device-profile.alarm-rule-details' | translate }}">
  99 + <mat-form-field class="mat-block row">
  100 + <mat-label translate>device-profile.alarm-details</mat-label>
  101 + <textarea matInput formControlName="alarmDetails" rows="5"></textarea>
  102 + </mat-form-field>
  103 + </mat-tab>
  104 + </mat-tab-group>
85 </div> 105 </div>
@@ -14,33 +14,8 @@ @@ -14,33 +14,8 @@
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 :host { 16 :host {
17 - .tb-condition-duration {  
18 - padding: 8px;  
19 - border: 1px groove rgba(0, 0, 0, .25);  
20 - border-radius: 4px;  
21 - }  
22 - .mat-expansion-panel.advanced-settings {  
23 - box-shadow: none;  
24 - border: none;  
25 - padding: 0;  
26 - }  
27 -}  
28 -  
29 -:host ::ng-deep {  
30 - .mat-expansion-panel.advanced-settings {  
31 - .mat-expansion-panel-body {  
32 - padding: 0;  
33 - }  
34 - }  
35 - .mat-form-field.duration-value-field {  
36 - .mat-form-field-infix {  
37 - width: 120px;  
38 - }  
39 - }  
40 - .mat-form-field.duration-unit-field {  
41 - .mat-form-field-infix {  
42 - width: 120px;  
43 - } 17 + .row {
  18 + margin-top: 1em;
44 } 19 }
45 } 20 }
46 21
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { ChangeDetectorRef, Component, forwardRef, Input, NgZone, OnInit } from '@angular/core'; 17 +import { Component, forwardRef, Input, OnInit } from '@angular/core';
18 import { 18 import {
19 ControlValueAccessor, 19 ControlValueAccessor,
20 FormBuilder, 20 FormBuilder,
@@ -25,9 +25,9 @@ import { @@ -25,9 +25,9 @@ import {
25 Validator, 25 Validator,
26 Validators 26 Validators
27 } from '@angular/forms'; 27 } from '@angular/forms';
28 -import { AlarmRule } from '@shared/models/device.models'; 28 +import { AlarmConditionType, AlarmConditionTypeTranslationMap, AlarmRule } from '@shared/models/device.models';
29 import { MatDialog } from '@angular/material/dialog'; 29 import { MatDialog } from '@angular/material/dialog';
30 -import { TimeUnit, timeUnitTranslationMap } from '../../../../../shared/models/time/time.models'; 30 +import { TimeUnit, timeUnitTranslationMap } from '@shared/models/time/time.models';
31 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 31 import { coerceBooleanProperty } from '@angular/cdk/coercion';
32 32
33 @Component({ 33 @Component({
@@ -51,6 +51,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -51,6 +51,9 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
51 51
52 timeUnits = Object.keys(TimeUnit); 52 timeUnits = Object.keys(TimeUnit);
53 timeUnitTranslations = timeUnitTranslationMap; 53 timeUnitTranslations = timeUnitTranslationMap;
  54 + alarmConditionTypes = Object.keys(AlarmConditionType);
  55 + AlarmConditionType = AlarmConditionType;
  56 + alarmConditionTypeTranslation = AlarmConditionTypeTranslationMap;
54 57
55 @Input() 58 @Input()
56 disabled: boolean; 59 disabled: boolean;
@@ -64,8 +67,6 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -64,8 +67,6 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
64 this.requiredValue = coerceBooleanProperty(value); 67 this.requiredValue = coerceBooleanProperty(value);
65 } 68 }
66 69
67 - enableDuration = false;  
68 -  
69 private modelValue: AlarmRule; 70 private modelValue: AlarmRule;
70 71
71 alarmRuleFormGroup: FormGroup; 72 alarmRuleFormGroup: FormGroup;
@@ -87,11 +88,18 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -87,11 +88,18 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
87 this.alarmRuleFormGroup = this.fb.group({ 88 this.alarmRuleFormGroup = this.fb.group({
88 condition: this.fb.group({ 89 condition: this.fb.group({
89 condition: [null, Validators.required], 90 condition: [null, Validators.required],
90 - durationUnit: [null],  
91 - durationValue: [null] 91 + spec: this.fb.group({
  92 + type: [AlarmConditionType.SIMPLE, Validators.required],
  93 + unit: [{value: null, disable: true}, Validators.required],
  94 + value: [{value: null, disable: true}, [Validators.required, Validators.min(1), Validators.max(2147483647), Validators.pattern('[0-9]*')]],
  95 + count: [{value: null, disable: true}, [Validators.required, Validators.min(1), Validators.max(2147483647), Validators.pattern('[0-9]*')]]
  96 + })
92 }, Validators.required), 97 }, Validators.required),
93 alarmDetails: [null] 98 alarmDetails: [null]
94 }); 99 });
  100 + this.alarmRuleFormGroup.get('condition.spec.type').valueChanges.subscribe((type) => {
  101 + this.updateValidators(type, true, true);
  102 + });
95 this.alarmRuleFormGroup.valueChanges.subscribe(() => { 103 this.alarmRuleFormGroup.valueChanges.subscribe(() => {
96 this.updateModel(); 104 this.updateModel();
97 }); 105 });
@@ -108,9 +116,13 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -108,9 +116,13 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
108 116
109 writeValue(value: AlarmRule): void { 117 writeValue(value: AlarmRule): void {
110 this.modelValue = value; 118 this.modelValue = value;
111 - this.enableDuration = value && !!value.condition.durationValue; 119 + if (this.modelValue?.condition?.spec === null) {
  120 + this.modelValue.condition.spec = {
  121 + type: AlarmConditionType.SIMPLE
  122 + };
  123 + }
112 this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false}); 124 this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false});
113 - this.updateValidators(); 125 + this.updateValidators(this.modelValue?.condition?.spec?.type);
114 } 126 }
115 127
116 public validate(c: FormControl) { 128 public validate(c: FormControl) {
@@ -121,31 +133,45 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -121,31 +133,45 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
121 }; 133 };
122 } 134 }
123 135
124 - public enableDurationChanged(enableDuration) {  
125 - this.enableDuration = enableDuration;  
126 - this.updateValidators(true, true);  
127 - }  
128 -  
129 - private updateValidators(resetDuration = false, emitEvent = false) {  
130 - if (this.enableDuration) {  
131 - this.alarmRuleFormGroup.get('condition').get('durationValue')  
132 - .setValidators([Validators.required, Validators.min(1), Validators.max(2147483647)]);  
133 - this.alarmRuleFormGroup.get('condition').get('durationUnit')  
134 - .setValidators([Validators.required]);  
135 - } else {  
136 - this.alarmRuleFormGroup.get('condition').get('durationValue')  
137 - .setValidators([]);  
138 - this.alarmRuleFormGroup.get('condition').get('durationUnit')  
139 - .setValidators([]);  
140 - if (resetDuration) {  
141 - this.alarmRuleFormGroup.get('condition').patchValue({  
142 - durationValue: null,  
143 - durationUnit: null  
144 - });  
145 - } 136 + private updateValidators(type: AlarmConditionType, resetDuration = false, emitEvent = false) {
  137 + switch (type) {
  138 + case AlarmConditionType.DURATION:
  139 + this.alarmRuleFormGroup.get('condition.spec.value').enable();
  140 + this.alarmRuleFormGroup.get('condition.spec.unit').enable();
  141 + this.alarmRuleFormGroup.get('condition.spec.count').disable();
  142 + if (resetDuration) {
  143 + this.alarmRuleFormGroup.get('condition.spec').patchValue({
  144 + count: null
  145 + });
  146 + }
  147 + break;
  148 + case AlarmConditionType.REPEATING:
  149 + this.alarmRuleFormGroup.get('condition.spec.count').enable();
  150 + this.alarmRuleFormGroup.get('condition.spec.value').disable();
  151 + this.alarmRuleFormGroup.get('condition.spec.unit').disable();
  152 + if (resetDuration) {
  153 + this.alarmRuleFormGroup.get('condition.spec').patchValue({
  154 + value: null,
  155 + unit: null
  156 + });
  157 + }
  158 + break;
  159 + case AlarmConditionType.SIMPLE:
  160 + this.alarmRuleFormGroup.get('condition.spec.value').disable();
  161 + this.alarmRuleFormGroup.get('condition.spec.unit').disable();
  162 + this.alarmRuleFormGroup.get('condition.spec.count').disable();
  163 + if (resetDuration) {
  164 + this.alarmRuleFormGroup.get('condition.spec').patchValue({
  165 + value: null,
  166 + unit: null,
  167 + count: null
  168 + });
  169 + }
  170 + break;
146 } 171 }
147 - this.alarmRuleFormGroup.get('condition').get('durationValue').updateValueAndValidity({emitEvent});  
148 - this.alarmRuleFormGroup.get('condition').get('durationUnit').updateValueAndValidity({emitEvent}); 172 + this.alarmRuleFormGroup.get('condition.spec.value').updateValueAndValidity({emitEvent});
  173 + this.alarmRuleFormGroup.get('condition.spec.unit').updateValueAndValidity({emitEvent});
  174 + this.alarmRuleFormGroup.get('condition.spec.count').updateValueAndValidity({emitEvent});
149 } 175 }
150 176
151 private updateModel() { 177 private updateModel() {
@@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
19 <div *ngFor="let createAlarmRuleControl of createAlarmRulesFormArray().controls; let $index = index; 19 <div *ngFor="let createAlarmRuleControl of createAlarmRulesFormArray().controls; let $index = index;
20 last as isLast;" fxLayout="row" fxLayoutAlign="start center" 20 last as isLast;" fxLayout="row" fxLayoutAlign="start center"
21 fxLayoutGap="8px" style="padding-bottom: 8px;" [formGroup]="createAlarmRuleControl"> 21 fxLayoutGap="8px" style="padding-bottom: 8px;" [formGroup]="createAlarmRuleControl">
22 - <div class="create-alarm-rule" fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> 22 + <div class="create-alarm-rule" fxFlex fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="start">
23 <mat-form-field class="severity mat-block" floatLabel="always" hideRequiredMarker> 23 <mat-form-field class="severity mat-block" floatLabel="always" hideRequiredMarker>
24 <mat-label translate>alarm.severity</mat-label> 24 <mat-label translate>alarm.severity</mat-label>
25 <mat-select formControlName="severity" 25 <mat-select formControlName="severity"
@@ -19,10 +19,6 @@ @@ -19,10 +19,6 @@
19 border: 1px groove rgba(0, 0, 0, .25); 19 border: 1px groove rgba(0, 0, 0, .25);
20 border-radius: 4px; 20 border-radius: 4px;
21 padding: 8px; 21 padding: 8px;
22 - .mat-form-field.severity {  
23 - border-right: 1px groove rgba(0, 0, 0, 0.25);  
24 - padding-right: 8px;  
25 - }  
26 } 22 }
27 } 23 }
28 24
@@ -210,10 +210,30 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D @@ -210,10 +210,30 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D
210 return transportConfiguration; 210 return transportConfiguration;
211 } 211 }
212 212
  213 +export enum AlarmConditionType {
  214 + SIMPLE = 'SIMPLE',
  215 + DURATION = 'DURATION',
  216 + REPEATING = 'REPEATING'
  217 +}
  218 +
  219 +export const AlarmConditionTypeTranslationMap = new Map<AlarmConditionType, string>(
  220 + [
  221 + [AlarmConditionType.SIMPLE, 'device-profile.condition-type-simple'],
  222 + [AlarmConditionType.DURATION, 'device-profile.condition-type-duration'],
  223 + [AlarmConditionType.REPEATING, 'device-profile.condition-type-repeating']
  224 + ]
  225 +);
  226 +
  227 +export interface AlarmConditionSpec{
  228 + type?: AlarmConditionType;
  229 + unit?: TimeUnit;
  230 + value?: number;
  231 + count?: number;
  232 +}
  233 +
213 export interface AlarmCondition { 234 export interface AlarmCondition {
214 condition: Array<KeyFilter>; 235 condition: Array<KeyFilter>;
215 - durationUnit?: TimeUnit;  
216 - durationValue?: number; 236 + spec?: AlarmConditionSpec;
217 } 237 }
218 238
219 export interface AlarmRule { 239 export interface AlarmRule {
@@ -834,6 +834,7 @@ @@ -834,6 +834,7 @@
834 "condition-duration-value": "Duration value", 834 "condition-duration-value": "Duration value",
835 "condition-duration-time-unit": "Time unit", 835 "condition-duration-time-unit": "Time unit",
836 "condition-duration-value-range": "Duration value should be in a range from 1 to 2147483647.", 836 "condition-duration-value-range": "Duration value should be in a range from 1 to 2147483647.",
  837 + "condition-duration-value-pattern": "Duration value should be integers.",
837 "condition-duration-value-required": "Duration value is required.", 838 "condition-duration-value-required": "Duration value is required.",
838 "condition-duration-time-unit-required": "Time unit is required.", 839 "condition-duration-time-unit-required": "Time unit is required.",
839 "advanced-settings": "Advanced settings", 840 "advanced-settings": "Advanced settings",
@@ -844,7 +845,18 @@ @@ -844,7 +845,18 @@
844 "alarm-details": "Alarm details", 845 "alarm-details": "Alarm details",
845 "alarm-rule-condition": "Alarm rule condition", 846 "alarm-rule-condition": "Alarm rule condition",
846 "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", 847 "enter-alarm-rule-condition-prompt": "Please add alarm rule condition",
847 - "edit-alarm-rule-condition": "Edit alarm rule condition" 848 + "edit-alarm-rule-condition": "Edit alarm rule condition",
  849 + "condition": "Condition",
  850 + "condition-type": "Condition type",
  851 + "condition-type-simple": "Simple",
  852 + "condition-type-duration": "Duration",
  853 + "condition-type-repeating": "Repeating",
  854 + "condition-type-required": "Condition type is required.",
  855 + "condition-repeating-value": "Count of events",
  856 + "condition-repeating-value-range": "Count of events should be in a range from 1 to 2147483647.",
  857 + "condition-repeating-value-pattern": "Count of events should be integers.",
  858 + "condition-repeating-value-required": "Count of events is required.",
  859 + "schedule": "Schedule"
848 }, 860 },
849 "dialog": { 861 "dialog": {
850 "close": "Close dialog" 862 "close": "Close dialog"