Commit b4566a93fde50c95ca5912ec6f6addf6ba7efa8f

Authored by Igor Kulikov
1 parent 5afc39eb

Add mobile dashboard to alarm rules

... ... @@ -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 }
... ...
... ... @@ -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;
... ...
... ... @@ -428,6 +428,7 @@ export interface CustomTimeSchedulerItem{
428 428 export interface AlarmRule {
429 429 condition: AlarmCondition;
430 430 alarmDetails?: string;
  431 + dashboardId?: DashboardId;
431 432 schedule?: AlarmSchedule;
432 433 }
433 434
... ...
... ... @@ -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.",
... ...