Commit b4566a93fde50c95ca5912ec6f6addf6ba7efa8f

Authored by Igor Kulikov
1 parent 5afc39eb

Add mobile dashboard to alarm rules

@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.DashboardId;
19 import org.thingsboard.server.common.data.validation.NoXss; 20 import org.thingsboard.server.common.data.validation.NoXss;
20 21
21 import javax.validation.Valid; 22 import javax.validation.Valid;
@@ -31,5 +32,6 @@ public class AlarmRule implements Serializable { @@ -31,5 +32,6 @@ public class AlarmRule implements Serializable {
31 // Advanced 32 // Advanced
32 @NoXss 33 @NoXss
33 private String alarmDetails; 34 private String alarmDetails;
  35 + private DashboardId dashboardId;
34 36
35 } 37 }
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus;
34 import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; 34 import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
35 import org.thingsboard.server.common.data.device.profile.AlarmConditionSpecType; 35 import org.thingsboard.server.common.data.device.profile.AlarmConditionSpecType;
36 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 36 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
  37 +import org.thingsboard.server.common.data.id.DashboardId;
37 import org.thingsboard.server.common.data.id.EntityId; 38 import org.thingsboard.server.common.data.id.EntityId;
38 import org.thingsboard.server.common.msg.TbMsg; 39 import org.thingsboard.server.common.msg.TbMsg;
39 import org.thingsboard.server.common.msg.TbMsgMetaData; 40 import org.thingsboard.server.common.msg.TbMsgMetaData;
@@ -269,16 +270,22 @@ class AlarmState { @@ -269,16 +270,22 @@ class AlarmState {
269 private JsonNode createDetails(AlarmRuleState ruleState) { 270 private JsonNode createDetails(AlarmRuleState ruleState) {
270 JsonNode alarmDetails; 271 JsonNode alarmDetails;
271 String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails(); 272 String alarmDetailsStr = ruleState.getAlarmRule().getAlarmDetails();
  273 + DashboardId dashboardId = ruleState.getAlarmRule().getDashboardId();
272 274
273 - if (StringUtils.isNotEmpty(alarmDetailsStr)) {  
274 - for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {  
275 - EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());  
276 - if (entityKeyValue != null) {  
277 - alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue)); 275 + if (StringUtils.isNotEmpty(alarmDetailsStr) || dashboardId != null) {
  276 + ObjectNode newDetails = JacksonUtil.newObjectNode();
  277 + if (StringUtils.isNotEmpty(alarmDetailsStr)) {
  278 + for (var keyFilter : ruleState.getAlarmRule().getCondition().getCondition()) {
  279 + EntityKeyValue entityKeyValue = dataSnapshot.getValue(keyFilter.getKey());
  280 + if (entityKeyValue != null) {
  281 + alarmDetailsStr = alarmDetailsStr.replaceAll(String.format("\\$\\{%s}", keyFilter.getKey().getKey()), getValueAsString(entityKeyValue));
  282 + }
278 } 283 }
  284 + newDetails.put("data", alarmDetailsStr);
  285 + }
  286 + if (dashboardId != null) {
  287 + newDetails.put("dashboardId", dashboardId.getId().toString());
279 } 288 }
280 - ObjectNode newDetails = JacksonUtil.newObjectNode();  
281 - newDetails.put("data", alarmDetailsStr);  
282 alarmDetails = newDetails; 289 alarmDetails = newDetails;
283 } else if (currentAlarm != null) { 290 } else if (currentAlarm != null) {
284 alarmDetails = currentAlarm.getDetails(); 291 alarmDetails = currentAlarm.getDetails();
@@ -46,8 +46,9 @@ @@ -46,8 +46,9 @@
46 formControlName="defaultRuleChainId"> 46 formControlName="defaultRuleChainId">
47 </tb-rule-chain-autocomplete> 47 </tb-rule-chain-autocomplete>
48 <tb-dashboard-autocomplete 48 <tb-dashboard-autocomplete
49 - placeholder="{{'device-profile.default-dashboard' | translate}}" 49 + placeholder="{{'device-profile.mobile-dashboard' | translate}}"
50 formControlName="defaultDashboardId"> 50 formControlName="defaultDashboardId">
  51 + <div tb-hint>{{'device-profile.mobile-dashboard-hint' | translate}}</div>
51 </tb-dashboard-autocomplete> 52 </tb-dashboard-autocomplete>
52 <tb-queue-type-list 53 <tb-queue-type-list
53 [queueType]="serviceType" 54 [queueType]="serviceType"
@@ -35,4 +35,16 @@ @@ -35,4 +35,16 @@
35 <mat-icon>{{ disabled ? 'visibility' : (alarmRuleFormGroup.get('alarmDetails').value ? 'edit' : 'add') }}</mat-icon> 35 <mat-icon>{{ disabled ? 'visibility' : (alarmRuleFormGroup.get('alarmDetails').value ? 'edit' : 'add') }}</mat-icon>
36 </button> 36 </button>
37 </div> 37 </div>
  38 + <div *ngIf="!disabled || alarmRuleFormGroup.get('dashboardId').value" fxLayout="column" fxLayoutAlign="start start"
  39 + fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="start center">
  40 + <span class="tb-alarm-rule-dashboard title">
  41 + {{ ('device-profile.alarm-rule-mobile-dashboard' | translate) + ': ' }}
  42 + </span>
  43 + <tb-dashboard-autocomplete class="tb-alarm-rule-dashboard dashboard"
  44 + floatLabel="never"
  45 + placeholder="{{ 'device-profile.alarm-rule-no-mobile-dashboard' | translate }}"
  46 + formControlName="dashboardId">
  47 + <div tb-hint>{{'device-profile.alarm-rule-mobile-dashboard-hint' | translate}}</div>
  48 + </tb-dashboard-autocomplete>
  49 + </div>
38 </div> 50 </div>
@@ -18,16 +18,24 @@ @@ -18,16 +18,24 @@
18 .row { 18 .row {
19 margin-top: 1em; 19 margin-top: 1em;
20 } 20 }
21 - .tb-alarm-rule-details { 21 + .tb-alarm-rule-details, .tb-alarm-rule-dashboard {
22 padding: 4px; 22 padding: 4px;
23 - cursor: pointer;  
24 - overflow: hidden;  
25 - white-space: nowrap;  
26 - text-overflow: ellipsis;  
27 &.title { 23 &.title {
28 opacity: 0.7; 24 opacity: 0.7;
29 overflow: visible; 25 overflow: visible;
30 } 26 }
31 } 27 }
  28 + .tb-alarm-rule-details {
  29 + overflow: hidden;
  30 + white-space: nowrap;
  31 + text-overflow: ellipsis;
  32 + cursor: pointer;
  33 + }
  34 + .tb-alarm-rule-dashboard {
  35 + &.dashboard {
  36 + width: 100%;
  37 + max-width: 350px;
  38 + }
  39 + }
32 } 40 }
33 41
@@ -34,6 +34,7 @@ import { @@ -34,6 +34,7 @@ import {
34 EditAlarmDetailsDialogData 34 EditAlarmDetailsDialogData
35 } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; 35 } from '@home/components/profile/alarm/edit-alarm-details-dialog.component';
36 import { EntityId } from '@shared/models/id/entity-id'; 36 import { EntityId } from '@shared/models/id/entity-id';
  37 +import { DashboardId } from '@shared/models/id/dashboard-id';
37 38
38 @Component({ 39 @Component({
39 selector: 'tb-alarm-rule', 40 selector: 'tb-alarm-rule',
@@ -92,7 +93,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -92,7 +93,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
92 this.alarmRuleFormGroup = this.fb.group({ 93 this.alarmRuleFormGroup = this.fb.group({
93 condition: [null, [Validators.required]], 94 condition: [null, [Validators.required]],
94 schedule: [null], 95 schedule: [null],
95 - alarmDetails: [null] 96 + alarmDetails: [null],
  97 + dashboardId: [null]
96 }); 98 });
97 this.alarmRuleFormGroup.valueChanges.subscribe(() => { 99 this.alarmRuleFormGroup.valueChanges.subscribe(() => {
98 this.updateModel(); 100 this.updateModel();
@@ -110,7 +112,11 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -110,7 +112,11 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
110 112
111 writeValue(value: AlarmRule): void { 113 writeValue(value: AlarmRule): void {
112 this.modelValue = value; 114 this.modelValue = value;
113 - this.alarmRuleFormGroup.reset(this.modelValue || undefined, {emitEvent: false}); 115 + const model = this.modelValue ? {
  116 + ...this.modelValue,
  117 + dashboardId: this.modelValue.dashboardId?.id
  118 + } : null;
  119 + this.alarmRuleFormGroup.reset(model || undefined, {emitEvent: false});
114 } 120 }
115 121
116 public openEditDetailsDialog($event: Event) { 122 public openEditDetailsDialog($event: Event) {
@@ -143,7 +149,7 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat @@ -143,7 +149,7 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat
143 private updateModel() { 149 private updateModel() {
144 const value = this.alarmRuleFormGroup.value; 150 const value = this.alarmRuleFormGroup.value;
145 if (this.modelValue) { 151 if (this.modelValue) {
146 - this.modelValue = {...this.modelValue, ...value}; 152 + this.modelValue = {...this.modelValue, ...value, dashboardId: value.dashboardId ? new DashboardId(value.dashboardId) : null};
147 this.propagateChange(this.modelValue); 153 this.propagateChange(this.modelValue);
148 } 154 }
149 } 155 }
@@ -60,8 +60,9 @@ @@ -60,8 +60,9 @@
60 formControlName="defaultRuleChainId"> 60 formControlName="defaultRuleChainId">
61 </tb-rule-chain-autocomplete> 61 </tb-rule-chain-autocomplete>
62 <tb-dashboard-autocomplete 62 <tb-dashboard-autocomplete
63 - placeholder="{{'device-profile.default-dashboard' | translate}}" 63 + placeholder="{{'device-profile.mobile-dashboard' | translate}}"
64 formControlName="defaultDashboardId"> 64 formControlName="defaultDashboardId">
  65 + <div tb-hint>{{'device-profile.mobile-dashboard-hint' | translate}}</div>
65 </tb-dashboard-autocomplete> 66 </tb-dashboard-autocomplete>
66 <tb-queue-type-list 67 <tb-queue-type-list
67 [queueType]="serviceType" 68 [queueType]="serviceType"
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<mat-form-field [formGroup]="selectDashboardFormGroup" class="mat-block"> 18 +<mat-form-field [formGroup]="selectDashboardFormGroup" class="mat-block" [floatLabel]="floatLabel">
19 <input matInput type="text" placeholder="{{ placeholder || ('dashboard.dashboard' | translate) }}" 19 <input matInput type="text" placeholder="{{ placeholder || ('dashboard.dashboard' | translate) }}"
20 #dashboardInput 20 #dashboardInput
21 formControlName="dashboard" 21 formControlName="dashboard"
@@ -29,6 +29,7 @@ import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; @@ -29,6 +29,7 @@ import { getCurrentAuthUser } from '@app/core/auth/auth.selectors';
29 import { Authority } from '@shared/models/authority.enum'; 29 import { Authority } from '@shared/models/authority.enum';
30 import { TranslateService } from '@ngx-translate/core'; 30 import { TranslateService } from '@ngx-translate/core';
31 import { coerceBooleanProperty } from '@angular/cdk/coercion'; 31 import { coerceBooleanProperty } from '@angular/cdk/coercion';
  32 +import { FloatLabelType } from '@angular/material/form-field/form-field';
32 33
33 @Component({ 34 @Component({
34 selector: 'tb-dashboard-autocomplete', 35 selector: 'tb-dashboard-autocomplete',
@@ -64,6 +65,9 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI @@ -64,6 +65,9 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI
64 @Input() 65 @Input()
65 customerId: string; 66 customerId: string;
66 67
  68 + @Input()
  69 + floatLabel: FloatLabelType = 'auto';
  70 +
67 private requiredValue: boolean; 71 private requiredValue: boolean;
68 get required(): boolean { 72 get required(): boolean {
69 return this.requiredValue; 73 return this.requiredValue;
@@ -428,6 +428,7 @@ export interface CustomTimeSchedulerItem{ @@ -428,6 +428,7 @@ export interface CustomTimeSchedulerItem{
428 export interface AlarmRule { 428 export interface AlarmRule {
429 condition: AlarmCondition; 429 condition: AlarmCondition;
430 alarmDetails?: string; 430 alarmDetails?: string;
  431 + dashboardId?: DashboardId;
431 schedule?: AlarmSchedule; 432 schedule?: AlarmSchedule;
432 } 433 }
433 434
@@ -1070,7 +1070,8 @@ @@ -1070,7 +1070,8 @@
1070 "profile-configuration": "Profile configuration", 1070 "profile-configuration": "Profile configuration",
1071 "transport-configuration": "Transport configuration", 1071 "transport-configuration": "Transport configuration",
1072 "default-rule-chain": "Default rule chain", 1072 "default-rule-chain": "Default rule chain",
1073 - "default-dashboard": "Default dashboard", 1073 + "mobile-dashboard": "Mobile dashboard",
  1074 + "mobile-dashboard-hint": "Used by mobile application as a device details dashboard",
1074 "select-queue-hint": "Select from a drop-down list or add a custom name.", 1075 "select-queue-hint": "Select from a drop-down list or add a custom name.",
1075 "delete-device-profile-title": "Are you sure you want to delete the device profile '{{deviceProfileName}}'?", 1076 "delete-device-profile-title": "Are you sure you want to delete the device profile '{{deviceProfileName}}'?",
1076 "delete-device-profile-text": "Be careful, after the confirmation the device profile and all related data will become unrecoverable.", 1077 "delete-device-profile-text": "Be careful, after the confirmation the device profile and all related data will become unrecoverable.",
@@ -1149,6 +1150,9 @@ @@ -1149,6 +1150,9 @@
1149 "advanced-settings": "Advanced settings", 1150 "advanced-settings": "Advanced settings",
1150 "alarm-rule-details": "Details", 1151 "alarm-rule-details": "Details",
1151 "add-alarm-rule-details": "Add details", 1152 "add-alarm-rule-details": "Add details",
  1153 + "alarm-rule-mobile-dashboard": "Mobile dashboard",
  1154 + "alarm-rule-mobile-dashboard-hint": "Used by mobile application as an alarm details dashboard",
  1155 + "alarm-rule-no-mobile-dashboard": "No dashboard selected",
1152 "propagate-alarm": "Propagate alarm", 1156 "propagate-alarm": "Propagate alarm",
1153 "alarm-rule-relation-types-list": "Relation types to propagate", 1157 "alarm-rule-relation-types-list": "Relation types to propagate",
1154 "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", 1158 "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.",