UI: Add notification template and rule trigger config for entities limit

This commit is contained in:
Vladyslav_Prykhodko 2023-03-21 15:03:30 +02:00
parent 26618fa3a2
commit ecdd49d672
9 changed files with 139 additions and 22 deletions

View File

@ -46,7 +46,7 @@
[notificationType]="entityType.NOTIFICATION_TARGET"> [notificationType]="entityType.NOTIFICATION_TARGET">
</tb-notification-table> </tb-notification-table>
</mat-tab> </mat-tab>
<mat-tab label="{{ 'notification.rules' | translate }}" *ngIf="isTenantAdmin()"> <mat-tab label="{{ 'notification.rules' | translate }}">
<tb-notification-table <tb-notification-table
[notificationType]="entityType.NOTIFICATION_RULE"> [notificationType]="entityType.NOTIFICATION_RULE">
</tb-notification-table> </tb-notification-table>

View File

@ -68,10 +68,6 @@ export class NotificationCenterComponent extends PageComponent {
.onEntityAction({event: $event, action: this.requestTab.isActive ? 'add' : 'add-without-update', entity: null}); .onEntityAction({event: $event, action: this.requestTab.isActive ? 'add' : 'add-without-update', entity: null});
} }
public isTenantAdmin(): boolean {
return this.authUser.authority === Authority.TENANT_ADMIN;
}
public isCustomerUser(): boolean { public isCustomerUser(): boolean {
return this.authUser.authority === Authority.CUSTOMER_USER; return this.authUser.authority === Authority.CUSTOMER_USER;
} }

View File

@ -392,6 +392,44 @@
</section> </section>
</form> </form>
</mat-step> </mat-step>
<mat-step *ngIf="ruleNotificationForm.get('triggerType').value === triggerType.ENTITIES_LIMIT"
[stepControl]="entitiesLimitTemplateForm">
<ng-template matStepLabel>{{ 'notification.entities-limit-trigger-settings' | translate }}</ng-template>
<form [formGroup]="entitiesLimitTemplateForm">
<fieldset class="fields-group tb-margin" formGroupName="triggerConfig">
<legend translate>notification.filter</legend>
<tb-entity-type-list formControlName="entityTypes"
ignoreAuthorityFilter
[allowedEntityTypes]="entityTypes">
</tb-entity-type-list>
<div class="limit-slider-container" fxLayout="row" fxLayoutAlign="start center"
fxLayout.xs="column" fxLayoutAlign.xs="stretch">
<label translate>notification.threshold</label>
<div fxLayout="row" fxLayoutAlign="start center" fxFlex>
<mat-slider fxFlex min="0" max="1" step="0.01" discrete [displayWith]="formatLabel">
<input matSliderThumb formControlName="threshold">
</mat-slider>
<mat-form-field class="limit-slider-value">
<input matInput formControlName="threshold" type="number" step="0.01"
[value]="entitiesLimitTemplateForm.get('triggerConfig.threshold').value"
min="0"
max="1"/>
</mat-form-field>
</div>
</div>
</fieldset>
</form>
<form [formGroup]="ruleNotificationForm">
<section formGroupName="additionalConfig">
<mat-form-field class="mat-block">
<mat-label translate>notification.description</mat-label>
<input matInput formControlName="description">
</mat-form-field>
</section>
</form>
</mat-step>
</mat-horizontal-stepper> </mat-horizontal-stepper>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>

View File

@ -66,6 +66,30 @@
} }
} }
.limit-slider-container {
.limit-slider-value {
margin-left: 16px;
min-width: 25px;
max-width: 100px;
}
mat-form-field input[type=number] {
text-align: center;
}
}
@media #{$mat-gt-sm} {
.history-time-input {
min-width: 364px;
}
.limit-slider-container {
> label {
margin-right: 16px;
width: min-content;
max-width: 40%;
}
}
}
::ng-deep { ::ng-deep {
.mat-mdc-dialog-content { .mat-mdc-dialog-content {

View File

@ -34,7 +34,7 @@ import { Router } from '@angular/router';
import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { NotificationService } from '@core/http/notification.service'; import { NotificationService } from '@core/http/notification.service';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { deepClone, deepTrim, isDefined } from '@core/utils'; import { deepClone, deepTrim, isDefined } from '@core/utils';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { map, takeUntil } from 'rxjs/operators';
@ -54,6 +54,10 @@ import {
TargetsNotificationDialogData TargetsNotificationDialogData
} from '@home/pages/notification-center/targets-table/target-notification-dialog.componet'; } from '@home/pages/notification-center/targets-table/target-notification-dialog.componet';
import { MatButton } from '@angular/material/button'; import { MatButton } from '@angular/material/button';
import { AuthState } from '@core/auth/auth.models';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { AuthUser } from '@shared/models/user.model';
import { Authority } from '@shared/models/authority.enum';
export interface RuleNotificationDialogData { export interface RuleNotificationDialogData {
rule?: NotificationRule; rule?: NotificationRule;
@ -80,9 +84,10 @@ export class RuleNotificationDialogComponent extends
alarmCommentTemplateForm: FormGroup; alarmCommentTemplateForm: FormGroup;
alarmAssignmentTemplateForm: FormGroup; alarmAssignmentTemplateForm: FormGroup;
ruleEngineEventsTemplateForm: FormGroup; ruleEngineEventsTemplateForm: FormGroup;
entitiesLimitTemplateForm: FormGroup;
triggerType = TriggerType; triggerType = TriggerType;
triggerTypes: TriggerType[] = Object.values(TriggerType); triggerTypes: TriggerType[];
triggerTypeTranslationMap = TriggerTypeTranslationMap; triggerTypeTranslationMap = TriggerTypeTranslationMap;
alarmSearchStatuses = [ alarmSearchStatuses = [
@ -106,7 +111,7 @@ export class RuleNotificationDialogComponent extends
componentLifecycleEventTranslationMap = ComponentLifecycleEventTranslationMap; componentLifecycleEventTranslationMap = ComponentLifecycleEventTranslationMap;
entityType = EntityType; entityType = EntityType;
entityTypes: EntityType[] = Object.values(EntityType); entityTypes = Array.from(entityTypeTranslations.keys()).filter(type => !!this.entityType[type]);
isAdd = true; isAdd = true;
selectedIndex = 0; selectedIndex = 0;
@ -118,6 +123,8 @@ export class RuleNotificationDialogComponent extends
private readonly ruleNotification: NotificationRule; private readonly ruleNotification: NotificationRule;
private triggerTypeFormsMap: Map<TriggerType, FormGroup>; private triggerTypeFormsMap: Map<TriggerType, FormGroup>;
private authState: AuthState = getCurrentAuthState(this.store);
private authUser: AuthUser = this.authState.authUser;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@ -130,6 +137,8 @@ export class RuleNotificationDialogComponent extends
private dialog: MatDialog) { private dialog: MatDialog) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.triggerTypes = this.allowTriggerTypes();
if (isDefined(data.isAdd)) { if (isDefined(data.isAdd)) {
this.isAdd = data.isAdd; this.isAdd = data.isAdd;
} }
@ -140,10 +149,10 @@ export class RuleNotificationDialogComponent extends
this.ruleNotificationForm = this.fb.group({ this.ruleNotificationForm = this.fb.group({
name: [null, Validators.required], name: [null, Validators.required],
templateId: [null, Validators.required], templateId: [null, Validators.required],
triggerType: [TriggerType.ALARM, Validators.required], triggerType: [this.isSysAdmin() ? TriggerType.ENTITIES_LIMIT : TriggerType.ALARM, Validators.required],
recipientsConfig: this.fb.group({ recipientsConfig: this.fb.group({
targets: [{value: null, disabled: true}, Validators.required], targets: [{value: null, disabled: !this.isSysAdmin()}, Validators.required],
escalationTable: [null, Validators.required] escalationTable: [{value: null, disabled: this.isSysAdmin()}, Validators.required]
}), }),
triggerConfig: [null], triggerConfig: [null],
additionalConfig: this.fb.group({ additionalConfig: this.fb.group({
@ -243,13 +252,21 @@ export class RuleNotificationDialogComponent extends
}) })
}); });
this.entitiesLimitTemplateForm = this.fb.group({
triggerConfig: this.fb.group({
entityTypes: [],
threshold: [.8, [Validators.min(0), Validators.max(1)]]
})
});
this.triggerTypeFormsMap = new Map<TriggerType, FormGroup>([ this.triggerTypeFormsMap = new Map<TriggerType, FormGroup>([
[TriggerType.ALARM, this.alarmTemplateForm], [TriggerType.ALARM, this.alarmTemplateForm],
[TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm], [TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm],
[TriggerType.DEVICE_INACTIVITY, this.deviceInactivityTemplateForm], [TriggerType.DEVICE_INACTIVITY, this.deviceInactivityTemplateForm],
[TriggerType.ENTITY_ACTION, this.entityActionTemplateForm], [TriggerType.ENTITY_ACTION, this.entityActionTemplateForm],
[TriggerType.ALARM_ASSIGNMENT, this.alarmAssignmentTemplateForm], [TriggerType.ALARM_ASSIGNMENT, this.alarmAssignmentTemplateForm],
[TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm] [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm],
[TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm]
]); ]);
if (data.isAdd || data.isCopy) { if (data.isAdd || data.isCopy) {
@ -368,4 +385,20 @@ export class RuleNotificationDialogComponent extends
countRecipientsChainConfig(): number { countRecipientsChainConfig(): number {
return Object.keys(this.ruleNotificationForm.get('recipientsConfig.escalationTable').value ?? {}).length; return Object.keys(this.ruleNotificationForm.get('recipientsConfig.escalationTable').value ?? {}).length;
} }
formatLabel(value: number): string {
const formatValue = (value * 100).toFixed();
return `${formatValue}%`;
}
private isSysAdmin(): boolean {
return this.authUser.authority === Authority.SYS_ADMIN;
}
private allowTriggerTypes(): TriggerType[] {
if (this.isSysAdmin()) {
return [TriggerType.ENTITIES_LIMIT];
}
return Object.values(TriggerType).filter(type => type !== TriggerType.ENTITIES_LIMIT);
}
} }

View File

@ -57,7 +57,7 @@ export class TemplateNotificationDialogComponent
dialogTitle = 'notification.edit-notification-template'; dialogTitle = 'notification.edit-notification-template';
notificationTypes = Object.keys(NotificationType) as NotificationType[]; notificationTypes: NotificationType[];
selectedIndex = 0; selectedIndex = 0;
hideSelectType = false; hideSelectType = false;
@ -76,6 +76,8 @@ export class TemplateNotificationDialogComponent
private translate: TranslateService) { private translate: TranslateService) {
super(store, router, dialogRef, fb); super(store, router, dialogRef, fb);
this.notificationTypes = this.allowNotificationType();
this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm'])
.pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical'));
@ -84,10 +86,6 @@ export class TemplateNotificationDialogComponent
this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvents: false}); this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvents: false});
} }
if (this.isSysAdmin()) {
this.hideSelectType = true;
}
if (data.isAdd || data.isCopy) { if (data.isAdd || data.isCopy) {
this.dialogTitle = 'notification.add-notification-template'; this.dialogTitle = 'notification.add-notification-template';
} }
@ -175,4 +173,11 @@ export class TemplateNotificationDialogComponent
private isSysAdmin(): boolean { private isSysAdmin(): boolean {
return this.authUser.authority === Authority.SYS_ADMIN; return this.authUser.authority === Authority.SYS_ADMIN;
} }
private allowNotificationType(): NotificationType[] {
if (this.isSysAdmin()) {
return [NotificationType.GENERAL, NotificationType.ENTITIES_LIMIT];
}
return Object.values(NotificationType).filter(type => type !== NotificationType.ENTITIES_LIMIT);
}
} }

View File

@ -27,6 +27,7 @@ import { MatAutocomplete } from '@angular/material/autocomplete';
import { MatChipGrid } from '@angular/material/chips'; import { MatChipGrid } from '@angular/material/chips';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { SubscriptSizing } from '@angular/material/form-field'; import { SubscriptSizing } from '@angular/material/form-field';
import { coerceBoolean } from '@shared/decorators/coerce-boolean';
interface EntityTypeInfo { interface EntityTypeInfo {
name: string; name: string;
@ -74,6 +75,7 @@ export class EntityTypeListComponent implements ControlValueAccessor, OnInit, Af
allowedEntityTypes: Array<EntityType | AliasEntityType>; allowedEntityTypes: Array<EntityType | AliasEntityType>;
@Input() @Input()
@coerceBoolean()
ignoreAuthorityFilter: boolean; ignoreAuthorityFilter: boolean;
@ViewChild('entityTypeInput') entityTypeInput: ElementRef<HTMLInputElement>; @ViewChild('entityTypeInput') entityTypeInput: ElementRef<HTMLInputElement>;

View File

@ -111,7 +111,7 @@ export interface NotificationRule extends Omit<BaseData<NotificationRuleId>, 'la
export type NotificationRuleTriggerConfig = Partial<AlarmNotificationRuleTriggerConfig & DeviceInactivityNotificationRuleTriggerConfig & export type NotificationRuleTriggerConfig = Partial<AlarmNotificationRuleTriggerConfig & DeviceInactivityNotificationRuleTriggerConfig &
EntityActionNotificationRuleTriggerConfig & AlarmCommentNotificationRuleTriggerConfig & AlarmAssignmentNotificationRuleTriggerConfig & EntityActionNotificationRuleTriggerConfig & AlarmCommentNotificationRuleTriggerConfig & AlarmAssignmentNotificationRuleTriggerConfig &
RuleEngineLifecycleEventNotificationRuleTriggerConfig>; RuleEngineLifecycleEventNotificationRuleTriggerConfig & EntitiesLimitNotificationRuleTriggerConfig>;
export interface AlarmNotificationRuleTriggerConfig { export interface AlarmNotificationRuleTriggerConfig {
alarmTypes?: Array<string>; alarmTypes?: Array<string>;
@ -160,6 +160,11 @@ export interface RuleEngineLifecycleEventNotificationRuleTriggerConfig {
onlyRuleNodeLifecycleFailures: ComponentLifecycleEvent; onlyRuleNodeLifecycleFailures: ComponentLifecycleEvent;
} }
export interface EntitiesLimitNotificationRuleTriggerConfig {
entityTypes: EntityType[];
threshold: number;
}
export enum ComponentLifecycleEvent { export enum ComponentLifecycleEvent {
STARTED = 'STARTED', STARTED = 'STARTED',
UPDATED = 'UPDATED', UPDATED = 'UPDATED',
@ -402,7 +407,8 @@ export enum NotificationType {
ENTITY_ACTION = 'ENTITY_ACTION', ENTITY_ACTION = 'ENTITY_ACTION',
ALARM_COMMENT = 'ALARM_COMMENT', ALARM_COMMENT = 'ALARM_COMMENT',
ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT', ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT',
RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT' RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT',
ENTITIES_LIMIT = 'ENTITIES_LIMIT'
} }
export const NotificationTypeIcons = new Map<NotificationType, string | null>([ export const NotificationTypeIcons = new Map<NotificationType, string | null>([
@ -411,7 +417,8 @@ export const NotificationTypeIcons = new Map<NotificationType, string | null>([
[NotificationType.ENTITY_ACTION, 'devices'], [NotificationType.ENTITY_ACTION, 'devices'],
[NotificationType.ALARM_COMMENT, 'comment'], [NotificationType.ALARM_COMMENT, 'comment'],
[NotificationType.ALARM_ASSIGNMENT, 'assignment_turned_in'], [NotificationType.ALARM_ASSIGNMENT, 'assignment_turned_in'],
[NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'] [NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'],
[NotificationType.ENTITIES_LIMIT, 'data_thresholding'],
]); ]);
export const AlarmSeverityNotificationColors = new Map<AlarmSeverity, string>( export const AlarmSeverityNotificationColors = new Map<AlarmSeverity, string>(
@ -481,7 +488,12 @@ export const NotificationTemplateTypeTranslateMap = new Map<NotificationType, No
name: 'notification.template-type.rule-engine-lifecycle-event', name: 'notification.template-type.rule-engine-lifecycle-event',
hint: 'notification.template-hint.rule-engine-lifecycle-event' hint: 'notification.template-hint.rule-engine-lifecycle-event'
} }
] ],
[NotificationType.ENTITIES_LIMIT,
{
name: 'notification.template-type.entities-limit',
hint: 'notification.template-hint.entities-limit'
}]
]); ]);
export enum TriggerType { export enum TriggerType {
@ -490,7 +502,8 @@ export enum TriggerType {
ENTITY_ACTION = 'ENTITY_ACTION', ENTITY_ACTION = 'ENTITY_ACTION',
ALARM_COMMENT = 'ALARM_COMMENT', ALARM_COMMENT = 'ALARM_COMMENT',
ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT', ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT',
RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT' RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT',
ENTITIES_LIMIT = 'ENTITIES_LIMIT'
} }
export const TriggerTypeTranslationMap = new Map<TriggerType, string>([ export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
@ -500,4 +513,5 @@ export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
[TriggerType.ALARM_COMMENT, 'notification.trigger.alarm-comment'], [TriggerType.ALARM_COMMENT, 'notification.trigger.alarm-comment'],
[TriggerType.ALARM_ASSIGNMENT, 'notification.trigger.alarm-assignment'], [TriggerType.ALARM_ASSIGNMENT, 'notification.trigger.alarm-assignment'],
[TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'notification.trigger.rule-engine-lifecycle-event'], [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'notification.trigger.rule-engine-lifecycle-event'],
[TriggerType.ENTITIES_LIMIT, 'notification.trigger.entities-limit'],
]); ]);

View File

@ -2779,6 +2779,7 @@
"edit-rule": "Edit rule", "edit-rule": "Edit rule",
"edit-template": "Edit template", "edit-template": "Edit template",
"email-settings": "Email settings", "email-settings": "Email settings",
"entities-limit-trigger-settings": "Entities limit trigger settings",
"entity-action-trigger-settings": "Entity action trigger settings", "entity-action-trigger-settings": "Entity action trigger settings",
"entity-type": "Entity type", "entity-type": "Entity type",
"failed-send": "Failed send", "failed-send": "Failed send",
@ -2890,6 +2891,7 @@
"alarm-assignment": "Available params: ${assigneeFirstName}, ${assigneeLastName}, ${assigneeEmail}, ${assigneeFirstName}, ${userName}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}", "alarm-assignment": "Available params: ${assigneeFirstName}, ${assigneeLastName}, ${assigneeEmail}, ${assigneeFirstName}, ${userName}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}",
"alarm-comment": "Available params: ${comment}, ${alarmType}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}", "alarm-comment": "Available params: ${comment}, ${alarmType}, ${alarmId}, ${alarmType}, ${alarmSeverity}, ${alarmStatus}, ${alarmOriginatorEntityType}, ${alarmOriginatorId}, ${alarmId}, ${alarmAction}",
"device-inactivity": "Available params: ${deviceName}, ${deviceLabel}, ${deviceType}, ${deviceId}", "device-inactivity": "Available params: ${deviceName}, ${deviceLabel}, ${deviceType}, ${deviceId}",
"entities-limit": "Available params: ${entityType}, ${currentCount}, ${limit}",
"entity-action": "Available params: ${actionType}, ${entityType}, ${entityName}, ${entityId}, ${originatorUserName}, ${originatorUserId}", "entity-action": "Available params: ${actionType}, ${entityType}, ${entityName}, ${entityId}, ${originatorUserName}, ${originatorUserId}",
"general": "Available params: ${recipientEmail}, ${recipientFirstName}, ${recipientLastName}", "general": "Available params: ${recipientEmail}, ${recipientFirstName}, ${recipientLastName}",
"rule-engine-lifecycle-event": "Available params: ${ruleChainName}, ${componentName}, ${eventType}, ${error}, ${ruleChainId}, ${componentId}" "rule-engine-lifecycle-event": "Available params: ${ruleChainName}, ${componentName}, ${eventType}, ${error}, ${ruleChainId}, ${componentId}"
@ -2901,6 +2903,7 @@
"alarm-assignment": "Alarm assignment", "alarm-assignment": "Alarm assignment",
"alarm-comment": "Alarm comment", "alarm-comment": "Alarm comment",
"device-inactivity": "Device inactivity", "device-inactivity": "Device inactivity",
"entities-limit": "Entities limit",
"entity-action": "Entity action", "entity-action": "Entity action",
"general": "General", "general": "General",
"rule-engine-lifecycle-event": "Rule engine lifecycle event" "rule-engine-lifecycle-event": "Rule engine lifecycle event"
@ -2910,11 +2913,13 @@
"tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles", "tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles",
"time": "Time", "time": "Time",
"track-rule-node-events": "Track rule node events", "track-rule-node-events": "Track rule node events",
"threshold": "Threshold",
"trigger": { "trigger": {
"alarm": "Alarm", "alarm": "Alarm",
"alarm-assignment": "Alarm assignment", "alarm-assignment": "Alarm assignment",
"alarm-comment": "Alarm comment", "alarm-comment": "Alarm comment",
"device-inactivity": "Device inactivity", "device-inactivity": "Device inactivity",
"entities-limit": "Entities limit",
"entity-action": "Entity action", "entity-action": "Entity action",
"trigger": "Trigger", "trigger": "Trigger",
"trigger-required": "Trigger is required", "trigger-required": "Trigger is required",