Commit b4566a93fde50c95ca5912ec6f6addf6ba7efa8f
1 parent
5afc39eb
Add mobile dashboard to alarm rules
Showing
11 changed files
with
65 additions
and
19 deletions
@@ -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 | } |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
@@ -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.", |