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 | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | +import org.thingsboard.server.common.data.id.DashboardId; | |
19 | 20 | import org.thingsboard.server.common.data.validation.NoXss; |
20 | 21 | |
21 | 22 | import javax.validation.Valid; |
... | ... | @@ -31,5 +32,6 @@ public class AlarmRule implements Serializable { |
31 | 32 | // Advanced |
32 | 33 | @NoXss |
33 | 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 | 34 | import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; |
35 | 35 | import org.thingsboard.server.common.data.device.profile.AlarmConditionSpecType; |
36 | 36 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
37 | +import org.thingsboard.server.common.data.id.DashboardId; | |
37 | 38 | import org.thingsboard.server.common.data.id.EntityId; |
38 | 39 | import org.thingsboard.server.common.msg.TbMsg; |
39 | 40 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
... | ... | @@ -269,16 +270,22 @@ class AlarmState { |
269 | 270 | private JsonNode createDetails(AlarmRuleState ruleState) { |
270 | 271 | JsonNode alarmDetails; |
271 | 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 | 289 | alarmDetails = newDetails; |
283 | 290 | } else if (currentAlarm != null) { |
284 | 291 | alarmDetails = currentAlarm.getDetails(); | ... | ... |
... | ... | @@ -46,8 +46,9 @@ |
46 | 46 | formControlName="defaultRuleChainId"> |
47 | 47 | </tb-rule-chain-autocomplete> |
48 | 48 | <tb-dashboard-autocomplete |
49 | - placeholder="{{'device-profile.default-dashboard' | translate}}" | |
49 | + placeholder="{{'device-profile.mobile-dashboard' | translate}}" | |
50 | 50 | formControlName="defaultDashboardId"> |
51 | + <div tb-hint>{{'device-profile.mobile-dashboard-hint' | translate}}</div> | |
51 | 52 | </tb-dashboard-autocomplete> |
52 | 53 | <tb-queue-type-list |
53 | 54 | [queueType]="serviceType" | ... | ... |
... | ... | @@ -35,4 +35,16 @@ |
35 | 35 | <mat-icon>{{ disabled ? 'visibility' : (alarmRuleFormGroup.get('alarmDetails').value ? 'edit' : 'add') }}</mat-icon> |
36 | 36 | </button> |
37 | 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 | 50 | </div> | ... | ... |
... | ... | @@ -18,16 +18,24 @@ |
18 | 18 | .row { |
19 | 19 | margin-top: 1em; |
20 | 20 | } |
21 | - .tb-alarm-rule-details { | |
21 | + .tb-alarm-rule-details, .tb-alarm-rule-dashboard { | |
22 | 22 | padding: 4px; |
23 | - cursor: pointer; | |
24 | - overflow: hidden; | |
25 | - white-space: nowrap; | |
26 | - text-overflow: ellipsis; | |
27 | 23 | &.title { |
28 | 24 | opacity: 0.7; |
29 | 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 | 34 | EditAlarmDetailsDialogData |
35 | 35 | } from '@home/components/profile/alarm/edit-alarm-details-dialog.component'; |
36 | 36 | import { EntityId } from '@shared/models/id/entity-id'; |
37 | +import { DashboardId } from '@shared/models/id/dashboard-id'; | |
37 | 38 | |
38 | 39 | @Component({ |
39 | 40 | selector: 'tb-alarm-rule', |
... | ... | @@ -92,7 +93,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat |
92 | 93 | this.alarmRuleFormGroup = this.fb.group({ |
93 | 94 | condition: [null, [Validators.required]], |
94 | 95 | schedule: [null], |
95 | - alarmDetails: [null] | |
96 | + alarmDetails: [null], | |
97 | + dashboardId: [null] | |
96 | 98 | }); |
97 | 99 | this.alarmRuleFormGroup.valueChanges.subscribe(() => { |
98 | 100 | this.updateModel(); |
... | ... | @@ -110,7 +112,11 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat |
110 | 112 | |
111 | 113 | writeValue(value: AlarmRule): void { |
112 | 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 | 122 | public openEditDetailsDialog($event: Event) { |
... | ... | @@ -143,7 +149,7 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat |
143 | 149 | private updateModel() { |
144 | 150 | const value = this.alarmRuleFormGroup.value; |
145 | 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 | 153 | this.propagateChange(this.modelValue); |
148 | 154 | } |
149 | 155 | } | ... | ... |
... | ... | @@ -60,8 +60,9 @@ |
60 | 60 | formControlName="defaultRuleChainId"> |
61 | 61 | </tb-rule-chain-autocomplete> |
62 | 62 | <tb-dashboard-autocomplete |
63 | - placeholder="{{'device-profile.default-dashboard' | translate}}" | |
63 | + placeholder="{{'device-profile.mobile-dashboard' | translate}}" | |
64 | 64 | formControlName="defaultDashboardId"> |
65 | + <div tb-hint>{{'device-profile.mobile-dashboard-hint' | translate}}</div> | |
65 | 66 | </tb-dashboard-autocomplete> |
66 | 67 | <tb-queue-type-list |
67 | 68 | [queueType]="serviceType" | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 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 | 19 | <input matInput type="text" placeholder="{{ placeholder || ('dashboard.dashboard' | translate) }}" |
20 | 20 | #dashboardInput |
21 | 21 | formControlName="dashboard" | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; |
29 | 29 | import { Authority } from '@shared/models/authority.enum'; |
30 | 30 | import { TranslateService } from '@ngx-translate/core'; |
31 | 31 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
32 | +import { FloatLabelType } from '@angular/material/form-field/form-field'; | |
32 | 33 | |
33 | 34 | @Component({ |
34 | 35 | selector: 'tb-dashboard-autocomplete', |
... | ... | @@ -64,6 +65,9 @@ export class DashboardAutocompleteComponent implements ControlValueAccessor, OnI |
64 | 65 | @Input() |
65 | 66 | customerId: string; |
66 | 67 | |
68 | + @Input() | |
69 | + floatLabel: FloatLabelType = 'auto'; | |
70 | + | |
67 | 71 | private requiredValue: boolean; |
68 | 72 | get required(): boolean { |
69 | 73 | return this.requiredValue; | ... | ... |
... | ... | @@ -1070,7 +1070,8 @@ |
1070 | 1070 | "profile-configuration": "Profile configuration", |
1071 | 1071 | "transport-configuration": "Transport configuration", |
1072 | 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 | 1075 | "select-queue-hint": "Select from a drop-down list or add a custom name.", |
1075 | 1076 | "delete-device-profile-title": "Are you sure you want to delete the device profile '{{deviceProfileName}}'?", |
1076 | 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 | 1150 | "advanced-settings": "Advanced settings", |
1150 | 1151 | "alarm-rule-details": "Details", |
1151 | 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 | 1156 | "propagate-alarm": "Propagate alarm", |
1153 | 1157 | "alarm-rule-relation-types-list": "Relation types to propagate", |
1154 | 1158 | "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", | ... | ... |