dt-common-device 7.4.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AlertSchema = exports.AlertModel = void 0;
37
37
  const mongoose_1 = __importStar(require("mongoose"));
38
38
  const alert_types_1 = require("./alert.types");
39
+ const issues_1 = require("../issues");
39
40
  // Main Alert schema
40
41
  const AlertSchema = new mongoose_1.Schema({
41
42
  category: {
@@ -50,7 +51,7 @@ const AlertSchema = new mongoose_1.Schema({
50
51
  },
51
52
  zoneId: {
52
53
  type: String,
53
- required: true,
54
+ required: false,
54
55
  index: true,
55
56
  },
56
57
  title: {
@@ -74,6 +75,12 @@ const AlertSchema = new mongoose_1.Schema({
74
75
  required: true,
75
76
  index: true,
76
77
  },
78
+ entitySubType: {
79
+ type: String,
80
+ enum: Object.values(issues_1.EntitySubType),
81
+ required: true,
82
+ index: true,
83
+ },
77
84
  severity: {
78
85
  type: String,
79
86
  enum: Object.values(alert_types_1.AlertSeverity),
@@ -1,45 +1,49 @@
1
1
  import { IAlertDocument } from "./Alert.model";
2
2
  import { CreateAlertData, UpdateAlertData, AlertCategory, AlertSeverity, IAlertQuery } from "./alert.types";
3
3
  import { AlertBuilder } from "./AlertBuilder";
4
+ import { IDevice } from "../entities/device/local/interfaces";
5
+ import { Source } from "../constants";
6
+ import { IUser } from "../entities/admin";
4
7
  export declare class AlertService {
5
8
  private readonly alertRepository;
6
9
  constructor();
7
10
  /**
8
11
  * Create an operations alert using AlertBuilder
9
12
  */
10
- raiseOperationsAlert(data: CreateAlertData): Promise<IAlertDocument>;
13
+ raiseOperationsAlert(data: CreateAlertData): Promise<IAlertDocument | null>;
11
14
  /**
12
15
  * Create a security alert using AlertBuilder
13
16
  */
14
- raiseSecurityAlert(data: CreateAlertData): Promise<IAlertDocument>;
15
- /**
16
- * Create an energy alert using AlertBuilder
17
- */
18
- raiseEnergyAlert(data: CreateAlertData): Promise<IAlertDocument>;
19
- /**
20
- * Create a device-specific alert using AlertBuilder
21
- */
17
+ raiseSecurityAlert(data: CreateAlertData): Promise<IAlertDocument | null>;
18
+ raiseDeviceAlert(data: CreateAlertData): Promise<IAlertDocument | null>;
22
19
  /**
23
20
  * Create a hub-specific alert using AlertBuilder
24
21
  */
25
- raiseHubAlert(data: CreateAlertData): Promise<IAlertDocument>;
26
- /**
27
- * Raise alert for device going offline (OPERATIONAL only)
28
- */
22
+ raiseHubAlert(data: CreateAlertData): Promise<IAlertDocument | null>;
23
+ raiseDoorLeftOpenAlert(device: IDevice, zone: any, openThreshold: number, source: Source): Promise<IAlertDocument | null>;
24
+ raiseNewDeviceAlert(device: {
25
+ deviceId: string;
26
+ deviceType: {
27
+ type: any;
28
+ };
29
+ propertyId: string;
30
+ name: string;
31
+ }, source: Source, description?: string): Promise<IAlertDocument | null>;
32
+ raiseDoorOpenFrequentAlert(device: IDevice, zone: any, accessTimes: number, openThreshold: number, source: Source): Promise<IAlertDocument | null>;
33
+ raiseDoorOpenOutsideBusinessHoursAlert(device: IDevice, zone: any, source: Source): Promise<IAlertDocument | null>;
29
34
  /**
30
35
  * Raise alert for device coming online (OPERATIONAL only)
31
36
  */
32
- /**
33
- * Raise alert for device battery level below threshold (READINESS + OPERATIONAL + ENERGY)
34
- */
35
- /**
36
- * Raise alert for device issue (jammed or malfunctioned) (READINESS + OPERATIONAL)
37
- */
37
+ raiseDeviceOnlineAlert(device: IDevice, source: Source): Promise<IAlertDocument | null>;
38
+ raiseLockAccessEmergencyCodeAlert(device: IDevice, zone: any, source: Source): Promise<IAlertDocument | null>;
39
+ raiseLockAccessMasterCodeAlert(device: IDevice, zone: any, source: Source): Promise<IAlertDocument | null>;
40
+ raiseSpecificDoorAccessAlert(user: IUser, device: IDevice, zone: any, source: Source): Promise<IAlertDocument | null>;
41
+ raiseGuestLockFirstAccessAlert(user: IUser, device: IDevice, zone: any, source: Source): Promise<IAlertDocument | null>;
38
42
  /**
39
43
  * Create a new alert with business logic validation
40
44
  * Accepts either a CreateAlertData object or an AlertBuilder instance
41
45
  */
42
- createAlert(alertData: CreateAlertData | AlertBuilder): Promise<IAlertDocument>;
46
+ createAlert(alertData: CreateAlertData | AlertBuilder): Promise<IAlertDocument | null>;
43
47
  /**
44
48
  * Get alert by ID with business logic
45
49
  */
@@ -77,6 +77,8 @@ const Alert_repository_1 = require("./Alert.repository");
77
77
  const Alert_model_1 = require("./Alert.model");
78
78
  const alert_types_1 = require("./alert.types");
79
79
  const AlertBuilder_1 = require("./AlertBuilder");
80
+ const constants_1 = require("../constants");
81
+ const audit_1 = require("../audit");
80
82
  let AlertService = (() => {
81
83
  let _classDecorators = [(0, typedi_1.Service)()];
82
84
  let _classDescriptor;
@@ -92,13 +94,15 @@ let AlertService = (() => {
92
94
  async raiseOperationsAlert(data) {
93
95
  const alertBuilder = AlertBuilder_1.AlertBuilder.createOperationsAlert()
94
96
  .setPropertyId(data.propertyId)
95
- .setZoneId(data.zoneId)
97
+ .setZoneId(data.zoneId || "")
96
98
  .setTitle(data.title)
97
99
  .setDescription(data.description);
98
100
  if (data.entityId)
99
101
  alertBuilder.setEntityId(data.entityId);
100
102
  if (data.entityType)
101
103
  alertBuilder.setEntityType(data.entityType);
104
+ if (data.entitySubType)
105
+ alertBuilder.setEntitySubType(data.entitySubType);
102
106
  if (data.createdBy)
103
107
  alertBuilder.setCreatedBy(data.createdBy);
104
108
  return await this.createAlert(alertBuilder);
@@ -109,56 +113,40 @@ let AlertService = (() => {
109
113
  async raiseSecurityAlert(data) {
110
114
  const alertBuilder = AlertBuilder_1.AlertBuilder.createSecurityAlert()
111
115
  .setPropertyId(data.propertyId)
112
- .setZoneId(data.zoneId)
116
+ .setZoneId(data.zoneId || "")
113
117
  .setTitle(data.title)
114
118
  .setDescription(data.description);
115
119
  if (data.entityId)
116
120
  alertBuilder.setEntityId(data.entityId);
117
121
  if (data.entityType)
118
122
  alertBuilder.setEntityType(data.entityType);
123
+ if (data.entitySubType)
124
+ alertBuilder.setEntitySubType(data.entitySubType);
119
125
  if (data.createdBy)
120
126
  alertBuilder.setCreatedBy(data.createdBy);
121
127
  return await this.createAlert(alertBuilder);
122
128
  }
123
- /**
124
- * Create an energy alert using AlertBuilder
125
- */
126
- async raiseEnergyAlert(data) {
127
- const alertBuilder = AlertBuilder_1.AlertBuilder.createEnergyAlert()
128
- .setPropertyId(data.propertyId)
129
- .setZoneId(data.zoneId)
129
+ async raiseDeviceAlert(data) {
130
+ const alertBuilder = AlertBuilder_1.AlertBuilder.createDeviceAlert(data.entityId || "", data.propertyId, data.zoneId || "")
130
131
  .setTitle(data.title)
131
132
  .setDescription(data.description);
132
- if (data.entityId)
133
- alertBuilder.setEntityId(data.entityId);
134
- if (data.entityType)
135
- alertBuilder.setEntityType(data.entityType);
133
+ if (data.category)
134
+ alertBuilder.setCategory(data.category);
135
+ if (data.severity)
136
+ alertBuilder.setSeverity(data.severity);
137
+ if (data.type)
138
+ alertBuilder.setType(data.type);
136
139
  if (data.createdBy)
137
140
  alertBuilder.setCreatedBy(data.createdBy);
141
+ if (data.entitySubType)
142
+ alertBuilder.setEntitySubType(data.entitySubType);
138
143
  return await this.createAlert(alertBuilder);
139
144
  }
140
- /**
141
- * Create a device-specific alert using AlertBuilder
142
- */
143
- // async raiseDeviceAlert(data: CreateAlertData): Promise<IAlertDocument> {
144
- // const alertBuilder = AlertBuilder.createDeviceAlert(
145
- // data.entityId || "",
146
- // data.propertyId,
147
- // data.zoneId
148
- // )
149
- // .setTitle(data.title)
150
- // .setDescription(data.description);
151
- // if (data.category) alertBuilder.setCategory(data.category);
152
- // if (data.severity) alertBuilder.setSeverity(data.severity);
153
- // if (data.type) alertBuilder.setType(data.type);
154
- // if (data.createdBy) alertBuilder.setCreatedBy(data.createdBy);
155
- // return await this.createAlert(alertBuilder);
156
- // }
157
145
  /**
158
146
  * Create a hub-specific alert using AlertBuilder
159
147
  */
160
148
  async raiseHubAlert(data) {
161
- const alertBuilder = AlertBuilder_1.AlertBuilder.createHubAlert(data.entityId || "", data.propertyId, data.zoneId)
149
+ const alertBuilder = AlertBuilder_1.AlertBuilder.createHubAlert(data.entityId || "", data.propertyId, data.zoneId || "")
162
150
  .setTitle(data.title)
163
151
  .setDescription(data.description);
164
152
  if (data.category)
@@ -169,125 +157,227 @@ let AlertService = (() => {
169
157
  alertBuilder.setType(data.type);
170
158
  if (data.createdBy)
171
159
  alertBuilder.setCreatedBy(data.createdBy);
160
+ if (data.entitySubType)
161
+ alertBuilder.setEntitySubType(data.entitySubType);
172
162
  return await this.createAlert(alertBuilder);
173
163
  }
174
- /**
175
- * Raise alert for device going offline (OPERATIONAL only)
176
- */
177
- // async raiseDeviceOfflineAlert(
178
- // device: IDevice,
179
- // source: Source,
180
- // reason?: string
181
- // ): Promise<IAlertDocument> {
182
- // return await this.raiseDeviceAlert({
183
- // entityId: device.deviceId,
184
- // propertyId: device.propertyId,
185
- // zoneId: device.zoneId,
186
- // title: "Device Offline",
187
- // description: `Device ${device.name} has gone offline. ${
188
- // reason ? `Reason: ${reason}` : ""
189
- // }`,
190
- // category: AlertCategory.OPERATIONS,
191
- // severity: AlertSeverity.HIGH,
192
- // createdBy: source,
193
- // entityType: EntityType.DEVICE,
194
- // type: AlertType.DEVICE_OFFLINE, // Using a relevant type for device offline
195
- // });
196
- // }
164
+ async raiseDoorLeftOpenAlert(device, zone, openThreshold, source) {
165
+ return await this.createAlert({
166
+ entityId: device.deviceId,
167
+ entityType: alert_types_1.EntityType.DEVICE,
168
+ entitySubType: device.deviceType.type,
169
+ propertyId: device.propertyId,
170
+ zoneId: device.zoneId,
171
+ title: "Door Left Open",
172
+ description: `${zone?.name} has a door left open, for more than ${openThreshold} minutes.`,
173
+ createdBy: source,
174
+ category: alert_types_1.AlertCategory.SECURITY,
175
+ severity: alert_types_1.AlertSeverity.CRITICAL,
176
+ type: alert_types_1.AlertType.DOOR_LEFT_OPEN,
177
+ });
178
+ }
179
+ async raiseNewDeviceAlert(device, source, description) {
180
+ return await this.createAlert({
181
+ entityId: device.deviceId,
182
+ entityType: alert_types_1.EntityType.DEVICE,
183
+ entitySubType: device?.deviceType?.type,
184
+ propertyId: device?.propertyId,
185
+ title: "New Device Detected",
186
+ description: description || `New device ${device?.name} has been detected.`,
187
+ createdBy: source,
188
+ category: alert_types_1.AlertCategory.OPERATIONS,
189
+ severity: alert_types_1.AlertSeverity.INFO,
190
+ type: alert_types_1.AlertType.ACCOUNT_NEW_DEVICE,
191
+ });
192
+ }
193
+ async raiseDoorOpenFrequentAlert(device, zone, accessTimes, openThreshold, source) {
194
+ return await this.createAlert({
195
+ entityId: device.deviceId,
196
+ entityType: alert_types_1.EntityType.DEVICE,
197
+ entitySubType: device.deviceType.type,
198
+ propertyId: device.propertyId,
199
+ zoneId: device.zoneId,
200
+ title: "Door Open Frequently",
201
+ description: `${zone?.name} has been opened ${accessTimes} times in the last ${openThreshold} minutes.`,
202
+ createdBy: source,
203
+ category: alert_types_1.AlertCategory.OPERATIONS,
204
+ severity: alert_types_1.AlertSeverity.HIGH,
205
+ type: alert_types_1.AlertType.DOOR_OPEN_FREQUENT,
206
+ });
207
+ }
208
+ async raiseDoorOpenOutsideBusinessHoursAlert(device, zone, source) {
209
+ return await this.createAlert({
210
+ entityId: device.deviceId,
211
+ entityType: alert_types_1.EntityType.DEVICE,
212
+ entitySubType: device.deviceType.type,
213
+ propertyId: device.propertyId,
214
+ zoneId: device.zoneId,
215
+ title: "Door Open Outside Business Hours",
216
+ description: `${zone?.name} has been opened outside business hours.`,
217
+ createdBy: source,
218
+ category: alert_types_1.AlertCategory.SECURITY,
219
+ severity: alert_types_1.AlertSeverity.HIGH,
220
+ type: alert_types_1.AlertType.DOOR_OPEN_OUTSIDE_BIZ_HOURS,
221
+ });
222
+ }
197
223
  /**
198
224
  * Raise alert for device coming online (OPERATIONAL only)
199
225
  */
200
- // async raiseDeviceOnlineAlert(
201
- // device: IDevice,
202
- // source: Source,
203
- // reason?: string
204
- // ): Promise<IAlertDocument> {
205
- // return await this.raiseDeviceAlert({
206
- // entityId: device.deviceId,
207
- // propertyId: device.propertyId,
208
- // zoneId: device.zoneId,
209
- // title: "Device Online",
210
- // description: `Device ${device.name} is now online. ${
211
- // reason ? `Reason: ${reason}` : ""
212
- // }`,
213
- // category: AlertCategory.OPERATIONS,
214
- // severity: AlertSeverity.INFO,
215
- // createdBy: source,
216
- // entityType: EntityType.DEVICE,
217
- // type: AlertType.DEVICE_ONLINE,
218
- // });
219
- // }
220
- /**
221
- * Raise alert for device battery level below threshold (READINESS + OPERATIONAL + ENERGY)
222
- */
223
- // async raiseDeviceBatteryAlert(
224
- // device: IDevice,
225
- // batteryLevel: number,
226
- // threshold: number,
227
- // source: Source
228
- // ): Promise<IAlertDocument> {
229
- // return await this.raiseDeviceAlert({
230
- // entityId: device.deviceId,
231
- // propertyId: device.propertyId,
232
- // zoneId: device.zoneId,
233
- // title: "Device Battery Low",
234
- // description: `Device ${device.name} (${device.deviceId}) battery level is ${batteryLevel}%, which is below the property threshold of ${threshold}%.`,
235
- // category: AlertCategory.ENERGY,
236
- // severity: AlertSeverity.HIGH,
237
- // createdBy: source,
238
- // entityType: EntityType.DEVICE,
239
- // type: AlertType.DEVICE_BATTERY_LOW,
240
- // });
241
- // }
242
- /**
243
- * Raise alert for device issue (jammed or malfunctioned) (READINESS + OPERATIONAL)
244
- */
245
- // async raiseDeviceIssueAlert(
246
- // device: IDevice,
247
- // issueType: string,
248
- // source: Source,
249
- // reason?: string
250
- // ): Promise<IAlertDocument> {
251
- // return await this.raiseDeviceAlert({
252
- // entityId: device.deviceId,
253
- // propertyId: device.propertyId,
254
- // zoneId: device.zoneId,
255
- // title: `Device Issue - ${issueType}`,
256
- // description: `Device ${device.name} has an issue: ${issueType}. ${
257
- // reason ? `Reason: ${reason}` : ""
258
- // }`,
259
- // category: AlertCategory.OPERATIONS,
260
- // severity: AlertSeverity.HIGH,
261
- // createdBy: source,
262
- // entityType: EntityType.DEVICE,
263
- // type: AlertType.DEVICE_ISSUE,
264
- // });
265
- // }
226
+ async raiseDeviceOnlineAlert(device, source) {
227
+ return await this.raiseDeviceAlert({
228
+ entityId: device.deviceId,
229
+ propertyId: device.propertyId,
230
+ zoneId: device.zoneId,
231
+ title: "Device Online",
232
+ description: `Device ${device.name} is now online.`,
233
+ category: alert_types_1.AlertCategory.OPERATIONS,
234
+ severity: alert_types_1.AlertSeverity.INFO,
235
+ createdBy: source,
236
+ entityType: alert_types_1.EntityType.DEVICE,
237
+ entitySubType: device.deviceType.type,
238
+ type: alert_types_1.AlertType.DEVICE_ONLINE,
239
+ });
240
+ }
241
+ async raiseLockAccessEmergencyCodeAlert(device, zone, source) {
242
+ return await this.createAlert({
243
+ entityId: device.deviceId,
244
+ entityType: alert_types_1.EntityType.DEVICE,
245
+ entitySubType: device.deviceType.type,
246
+ propertyId: device.propertyId,
247
+ zoneId: device.zoneId,
248
+ title: "Emergency Code Used",
249
+ description: `${zone?.name} has been accessed using the emergency code.`,
250
+ createdBy: source,
251
+ category: alert_types_1.AlertCategory.SECURITY,
252
+ severity: alert_types_1.AlertSeverity.HIGH,
253
+ type: alert_types_1.AlertType.LOCK_ACCESS_EMERGENCY_CODE,
254
+ });
255
+ }
256
+ async raiseLockAccessMasterCodeAlert(device, zone, source) {
257
+ return await this.createAlert({
258
+ entityId: device.deviceId,
259
+ entityType: alert_types_1.EntityType.DEVICE,
260
+ entitySubType: device.deviceType.type,
261
+ propertyId: device.propertyId,
262
+ zoneId: device.zoneId,
263
+ title: "Master Code Used",
264
+ description: `${zone?.name} has been accessed using the master code.`,
265
+ createdBy: source,
266
+ category: alert_types_1.AlertCategory.SECURITY,
267
+ severity: alert_types_1.AlertSeverity.HIGH,
268
+ type: alert_types_1.AlertType.LOCK_ACCESS_MASTER_CODE,
269
+ });
270
+ }
271
+ async raiseSpecificDoorAccessAlert(user, device, zone, source) {
272
+ return await this.createAlert({
273
+ entityId: device.deviceId,
274
+ entityType: alert_types_1.EntityType.DEVICE,
275
+ entitySubType: device.deviceType.type,
276
+ propertyId: device.propertyId,
277
+ zoneId: device.zoneId,
278
+ title: "Specific Door Access",
279
+ description: `${user?.firstName} ${user?.lastName} has accessed ${zone?.name}.`,
280
+ createdBy: source,
281
+ category: alert_types_1.AlertCategory.OTHER,
282
+ severity: alert_types_1.AlertSeverity.INFO,
283
+ type: alert_types_1.AlertType.SPECIFIC_DOOR_ACCESS,
284
+ });
285
+ }
286
+ async raiseGuestLockFirstAccessAlert(user, device, zone, source) {
287
+ return await this.createAlert({
288
+ entityId: device.deviceId,
289
+ entityType: alert_types_1.EntityType.DEVICE,
290
+ entitySubType: device.deviceType.type,
291
+ propertyId: device.propertyId,
292
+ zoneId: device.zoneId,
293
+ title: "Guest Used Code for First Time",
294
+ description: `${user?.firstName} ${user?.lastName} has used the code for the first time to access ${zone?.name}.`,
295
+ createdBy: source,
296
+ category: alert_types_1.AlertCategory.OTHER,
297
+ severity: alert_types_1.AlertSeverity.INFO,
298
+ type: alert_types_1.AlertType.GUEST_LOCK_FIRST_ACCESS,
299
+ });
300
+ }
266
301
  /**
267
302
  * Create a new alert with business logic validation
268
303
  * Accepts either a CreateAlertData object or an AlertBuilder instance
269
304
  */
270
305
  async createAlert(alertData) {
271
- let processedAlertData;
272
- // Handle AlertBuilder instance
273
- if (alertData instanceof AlertBuilder_1.AlertBuilder) {
274
- processedAlertData = alertData.build();
275
- }
276
- else {
277
- processedAlertData = alertData;
278
- }
279
- // Business logic: Validate alert data
280
- this.validateAlertData(processedAlertData);
281
- // Business logic: Set default severity if not provided
282
- if (!processedAlertData.severity) {
283
- processedAlertData.severity = this.determineDefaultSeverity(processedAlertData.category, processedAlertData.type);
284
- }
285
- // Business logic: Validate snooze date is in the future
286
- if (processedAlertData.snoozeUntil &&
287
- processedAlertData.snoozeUntil <= new Date()) {
288
- throw new Error("Snooze date must be in the future");
306
+ try {
307
+ let processedAlertData;
308
+ // Handle AlertBuilder instance
309
+ if (alertData instanceof AlertBuilder_1.AlertBuilder) {
310
+ processedAlertData = alertData.build();
311
+ }
312
+ else {
313
+ processedAlertData = alertData;
314
+ }
315
+ // Business logic: Validate alert data
316
+ this.validateAlertData(processedAlertData);
317
+ // Business logic: Set default severity if not provided
318
+ if (!processedAlertData.severity) {
319
+ processedAlertData.severity = this.determineDefaultSeverity(processedAlertData.category, processedAlertData.type);
320
+ }
321
+ // Business logic: Validate snooze date is in the future
322
+ if (processedAlertData.snoozeUntil &&
323
+ processedAlertData.snoozeUntil <= new Date()) {
324
+ throw new Error("Snooze date must be in the future");
325
+ }
326
+ const existingAlert = await this.queryAlerts({
327
+ propertyId: processedAlertData.propertyId,
328
+ zoneId: processedAlertData.zoneId,
329
+ entityId: processedAlertData.entityId,
330
+ entityType: processedAlertData.entityType,
331
+ entitySubType: processedAlertData.entitySubType,
332
+ });
333
+ if (existingAlert.length > 0) {
334
+ await (0, audit_1.pushAudit)({
335
+ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.SKIPPED,
336
+ auditData: {
337
+ reason: "Alert already exists",
338
+ resource: audit_1.Resource.ALERT,
339
+ source: processedAlertData.createdBy,
340
+ propertyId: processedAlertData.propertyId,
341
+ entityId: processedAlertData.entityId,
342
+ entityType: processedAlertData.entityType,
343
+ entitySubType: processedAlertData.entitySubType,
344
+ },
345
+ });
346
+ return null;
347
+ }
348
+ const alert = await this.alertRepository.create(processedAlertData);
349
+ await (0, audit_1.pushAudit)({
350
+ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.SUCCESS,
351
+ auditData: {
352
+ resource: audit_1.Resource.ALERT,
353
+ source: constants_1.Source.USER,
354
+ propertyId: processedAlertData.propertyId,
355
+ zoneId: processedAlertData.zoneId,
356
+ entityId: processedAlertData.entityId,
357
+ entityType: processedAlertData.entityType,
358
+ entitySubType: processedAlertData.entitySubType,
359
+ type: processedAlertData.type,
360
+ createdBy: processedAlertData.createdBy,
361
+ createdAt: new Date(),
362
+ },
363
+ });
364
+ return alert;
365
+ }
366
+ catch (error) {
367
+ await (0, audit_1.pushAudit)({
368
+ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.FAILED,
369
+ auditData: {
370
+ resource: audit_1.Resource.ALERT,
371
+ source: constants_1.Source.USER,
372
+ propertyId: alertData?.propertyId || "",
373
+ zoneId: alertData?.zoneId || "",
374
+ errorMessage: error.message,
375
+ error: error,
376
+ createdBy: alertData?.createdBy || "",
377
+ },
378
+ });
379
+ throw error;
289
380
  }
290
- return await this.alertRepository.create(processedAlertData);
291
381
  }
292
382
  /**
293
383
  * Get alert by ID with business logic
@@ -1,3 +1,4 @@
1
+ import { EntitySubType } from "../issues";
1
2
  import { CreateAlertData, AlertCategory, AlertSeverity, EntityType, AlertType } from "./alert.types";
2
3
  /**
3
4
  * AlertBuilder - A builder pattern implementation for constructing CreateAlertData objects
@@ -48,6 +49,10 @@ export declare class AlertBuilder {
48
49
  * Sets the entity type
49
50
  */
50
51
  setEntityType(entityType: EntityType): this;
52
+ /**
53
+ * Sets the entity sub type
54
+ */
55
+ setEntitySubType(entitySubType: EntitySubType): this;
51
56
  /**
52
57
  * Sets the alert severity (optional, defaults to LOW)
53
58
  */
@@ -88,6 +88,13 @@ class AlertBuilder {
88
88
  this.data.entityType = entityType;
89
89
  return this;
90
90
  }
91
+ /**
92
+ * Sets the entity sub type
93
+ */
94
+ setEntitySubType(entitySubType) {
95
+ this.data.entitySubType = entitySubType;
96
+ return this;
97
+ }
91
98
  /**
92
99
  * Sets the alert severity (optional, defaults to LOW)
93
100
  */
@@ -1,4 +1,4 @@
1
- import { EntityType } from "../issues/issue.types";
1
+ import { EntitySubType, EntityType } from "../issues/issue.types";
2
2
  export declare enum AlertCategory {
3
3
  OPERATIONS = "OPERATIONS",
4
4
  SECURITY = "SECURITY",
@@ -20,18 +20,20 @@ export declare enum AlertType {
20
20
  LOCK_ACCESS_EMERGENCY_CODE = "LOCK_ACCESS_EMERGENCY_CODE",
21
21
  LOCK_ACCESS_MASTER_CODE = "LOCK_ACCESS_MASTER_CODE",
22
22
  SPECIFIC_DOOR_ACCESS = "SPECIFIC_DOOR_ACCESS",
23
- GUEST_LOCK_FIRST_ACCESS = "GUEST_LOCK_FIRST_ACCESS"
23
+ GUEST_LOCK_FIRST_ACCESS = "GUEST_LOCK_FIRST_ACCESS",
24
+ DEVICE_ONLINE = "DEVICE_ONLINE"
24
25
  }
25
26
  export interface AlertDocument {
26
27
  _id: string;
27
28
  category: AlertCategory;
28
29
  propertyId: string;
29
- zoneId: string;
30
+ zoneId?: string;
30
31
  type: AlertType;
31
32
  title: string;
32
33
  description: string;
33
34
  entityId?: string;
34
35
  entityType: EntityType;
36
+ entitySubType: EntitySubType;
35
37
  severity: AlertSeverity;
36
38
  isRead: boolean;
37
39
  isActive: boolean;
@@ -45,11 +47,12 @@ export interface AlertDocument {
45
47
  export interface CreateAlertData {
46
48
  category: AlertCategory;
47
49
  propertyId: string;
48
- zoneId: string;
50
+ zoneId?: string;
49
51
  title: string;
50
52
  description: string;
51
53
  entityId?: string;
52
54
  entityType: EntityType;
55
+ entitySubType: EntitySubType;
53
56
  severity?: AlertSeverity;
54
57
  type?: AlertType;
55
58
  createdBy?: string;
@@ -59,8 +62,10 @@ export interface UpdateAlertData {
59
62
  category?: AlertCategory;
60
63
  title?: string;
61
64
  description?: string;
65
+ zoneId?: string;
62
66
  entityId?: string;
63
67
  entityType?: EntityType;
68
+ entitySubType?: EntitySubType;
64
69
  severity?: AlertSeverity;
65
70
  type?: AlertType;
66
71
  isRead?: boolean;
@@ -74,6 +79,7 @@ export interface IAlertQuery {
74
79
  category?: AlertCategory;
75
80
  severity?: AlertSeverity;
76
81
  entityType?: EntityType;
82
+ entitySubType?: EntitySubType;
77
83
  entityId?: string;
78
84
  type?: AlertType | AlertType[];
79
85
  isActive?: boolean;
@@ -26,6 +26,7 @@ var AlertType;
26
26
  AlertType["LOCK_ACCESS_MASTER_CODE"] = "LOCK_ACCESS_MASTER_CODE";
27
27
  AlertType["SPECIFIC_DOOR_ACCESS"] = "SPECIFIC_DOOR_ACCESS";
28
28
  AlertType["GUEST_LOCK_FIRST_ACCESS"] = "GUEST_LOCK_FIRST_ACCESS";
29
+ AlertType["DEVICE_ONLINE"] = "DEVICE_ONLINE";
29
30
  })(AlertType || (exports.AlertType = AlertType = {}));
30
31
  // Re-export EntityType from issue.types.ts to avoid duplication
31
32
  var issue_types_1 = require("../issues/issue.types");
@@ -13,5 +13,6 @@ export declare enum Source {
13
13
  CLOUD_WEBHOOK = "cloud-webhook",
14
14
  HEARTBEAT = "heartbeat",
15
15
  CODE_ACTION = "code-action",
16
- DEVICE_ACTION = "device-action"
16
+ DEVICE_ACTION = "device-action",
17
+ SYSTEM = "system"
17
18
  }
@@ -17,4 +17,5 @@ var Source;
17
17
  Source["HEARTBEAT"] = "heartbeat";
18
18
  Source["CODE_ACTION"] = "code-action";
19
19
  Source["DEVICE_ACTION"] = "device-action";
20
+ Source["SYSTEM"] = "system";
20
21
  })(Source || (exports.Source = Source = {}));
@@ -74,7 +74,7 @@ export interface IQueryDevice {
74
74
  deviceIds?: string[];
75
75
  propertyId?: string;
76
76
  hubId?: string;
77
- deviceType?: string;
77
+ deviceType?: string | string[];
78
78
  excludeDeviceType?: string | string[];
79
79
  connectionId?: string;
80
80
  }
@@ -11,7 +11,6 @@ export declare class DeviceRepository {
11
11
  deleteDevice(deviceId: string): Promise<void>;
12
12
  getDevices(deviceIds: string[], withHubDetails?: boolean): Promise<IDevice[]>;
13
13
  getPropertyDevices(propertyId: string, selectDeviceId?: boolean, type?: string, withHubDetails?: boolean): Promise<IDevice[]>;
14
- getPropertyDeviceIds(propertyId: string, selectDeviceId: boolean | undefined, manufacturer: string): Promise<any>;
15
14
  getState(deviceId: string): Promise<any>;
16
15
  setState(deviceId: string, state: any): Promise<any>;
17
16
  getStatus(deviceId: string): Promise<Record<string, any>>;
@@ -129,18 +129,6 @@ let DeviceRepository = (() => {
129
129
  throw new Error(`Failed to get property devices: ${error.message || "Unknown error"}`);
130
130
  }
131
131
  }
132
- async getPropertyDeviceIds(propertyId, selectDeviceId = false, manufacturer) {
133
- try {
134
- const response = await this.axiosInstance.get(`/devices`, {
135
- params: { propertyId, selectDeviceId, manufacturer },
136
- });
137
- return response.data;
138
- }
139
- catch (error) {
140
- (0, config_1.getConfig)().LOGGER.error(`Failed to get property device IDs for ${propertyId}:`, error);
141
- throw new Error(`Failed to get property device IDs: ${error.message || "Unknown error"}`);
142
- }
143
- }
144
132
  async getState(deviceId) {
145
133
  try {
146
134
  const response = await this.axiosInstance.get(`/devices/${deviceId}/state`);
@@ -12,7 +12,6 @@ export declare class LocalDeviceService {
12
12
  getDevice(deviceId: string, withHubDetails?: boolean): Promise<IDevice>;
13
13
  getDevices(deviceIds: string[], withHubDetails?: boolean): Promise<IDevice[]>;
14
14
  getPropertyDevices(propertyId: string, selectDeviceId?: boolean, type?: string, withHubDetails?: boolean): Promise<IDevice[]>;
15
- getPropertyDeviceIds(propertyId: string, selectDeviceId: boolean | undefined, type: string): Promise<any>;
16
15
  updateDevice(deviceId: string, body: Partial<IDevice>, auditBody: IAuditProperties): Promise<any>;
17
16
  updateDevices(query: any, updateData: any): Promise<any>;
18
17
  deleteDevice(deviceId: string, auditBody: IAuditProperties): Promise<any>;
@@ -29,7 +28,6 @@ export declare class LocalDeviceService {
29
28
  private shouldUpdateBatteryLevel;
30
29
  updateBatteryState(deviceId: string, batteryState: any): Promise<void>;
31
30
  private getPropertyBatteryThreshold;
32
- private checkForDeviceMalfunctions;
33
31
  getMetaData(deviceId: string): Promise<any>;
34
32
  setMetaData(deviceId: string, metaData: Record<string, any>, auditBody: IAuditProperties): Promise<any>;
35
33
  getDevicesByZone(zoneId: string): Promise<import("../interfaces").IDtDevice[]>;
@@ -121,12 +121,6 @@ let LocalDeviceService = (() => {
121
121
  }
122
122
  return await this.deviceRepository.getPropertyDevices(propertyId, selectDeviceId, type, withHubDetails);
123
123
  }
124
- async getPropertyDeviceIds(propertyId, selectDeviceId = false, type) {
125
- if (!propertyId) {
126
- throw new Error("Property ID is required");
127
- }
128
- return await this.deviceRepository.getPropertyDeviceIds(propertyId, selectDeviceId, type);
129
- }
130
124
  async updateDevice(deviceId, body, auditBody) {
131
125
  if (!deviceId) {
132
126
  throw new Error("Device ID is required");
@@ -139,6 +133,13 @@ let LocalDeviceService = (() => {
139
133
  const auditType = body?.status?.online
140
134
  ? constants_1.DT_EVENT_TYPES.DEVICE.STATUS.ONLINE
141
135
  : constants_1.DT_EVENT_TYPES.DEVICE.STATUS.OFFLINE;
136
+ if (body?.status?.online) {
137
+ await this.handleOnlineStatus(device, device.status, body.status, auditBody?.source || Service_1.Source.SYSTEM, auditBody);
138
+ }
139
+ else {
140
+ await this.handleOfflineStatus(device, device.status, body.status, auditBody?.source || Service_1.Source.SYSTEM, auditBody);
141
+ }
142
+ await this.setBatteryLevel(deviceId, body.state?.batteryPercentage?.value, auditBody?.source || Service_1.Source.SYSTEM, auditBody);
142
143
  (0, audit_1.pushAudit)({
143
144
  auditType,
144
145
  auditData: {
@@ -165,7 +166,20 @@ let LocalDeviceService = (() => {
165
166
  ? constants_1.DT_EVENT_TYPES.DEVICE.STATUS.ONLINE
166
167
  : constants_1.DT_EVENT_TYPES.DEVICE.STATUS.OFFLINE;
167
168
  const devices = await this.deviceRepository.queryDevices(query);
168
- devices.forEach((device) => {
169
+ for (const device of devices) {
170
+ const auditBody = {
171
+ source: Service_1.Source.DEVICE_ACTION,
172
+ resource: IAuditProperties_1.Resource.DEVICE,
173
+ propertyId: device.propertyId,
174
+ zoneId: device.zoneId,
175
+ deviceId: device.deviceId,
176
+ };
177
+ if (updateData?.status?.online) {
178
+ await this.handleOnlineStatus(device, device.status, updateData.status, Service_1.Source.DEVICE_ACTION, auditBody);
179
+ }
180
+ else {
181
+ await this.handleOfflineStatus(device, device.status, updateData.status, Service_1.Source.DEVICE_ACTION, auditBody);
182
+ }
169
183
  (0, audit_1.pushAudit)({
170
184
  auditType,
171
185
  auditData: {
@@ -181,7 +195,7 @@ let LocalDeviceService = (() => {
181
195
  newStatus: updateData.status,
182
196
  },
183
197
  });
184
- });
198
+ }
185
199
  }
186
200
  return await this.deviceRepository.updateDevices(query, updateData);
187
201
  }
@@ -224,8 +238,8 @@ let LocalDeviceService = (() => {
224
238
  const oldStatus = device.status;
225
239
  const currentTime = new Date().toISOString();
226
240
  // Determine if the new status is ONLINE or OFFLINE
227
- const isNewStatusOnline = newStatus.liveStatus === "ONLINE";
228
- const isNewStatusOffline = newStatus.liveStatus === "OFFLINE";
241
+ const isNewStatusOnline = newStatus?.liveStatus === "ONLINE";
242
+ const isNewStatusOffline = newStatus?.liveStatus === "OFFLINE";
229
243
  if (isNewStatusOffline) {
230
244
  // New Status = OFFLINE
231
245
  await this.handleOfflineStatus(device, oldStatus, newStatus, source, auditBody, reason, currentTime);
@@ -249,6 +263,15 @@ let LocalDeviceService = (() => {
249
263
  const eventType = newStatus.online
250
264
  ? constants_1.DT_EVENT_TYPES.DEVICE.STATUS.ONLINE
251
265
  : constants_1.DT_EVENT_TYPES.DEVICE.STATUS.OFFLINE;
266
+ const devices = await this.deviceRepository.queryDevices(query);
267
+ for (const device of devices) {
268
+ if (newStatus.online) {
269
+ await this.handleOnlineStatus(device, device.status, newStatus, source, auditBody);
270
+ }
271
+ else {
272
+ await this.handleOfflineStatus(device, device.status, newStatus, source, auditBody);
273
+ }
274
+ }
252
275
  await this.deviceRepository.setStatusMany(query, newStatus);
253
276
  await this.eventHandler.onStatusChangeMany(query, newStatus, auditBody, eventType);
254
277
  }
@@ -286,8 +309,6 @@ let LocalDeviceService = (() => {
286
309
  };
287
310
  await this.deviceRepository.setStatus(device.deviceId, newStatus);
288
311
  await this.eventHandler.onStatusChange(device.deviceId, newStatus, auditBody, constants_1.DT_EVENT_TYPES.DEVICE.STATUS.OFFLINE);
289
- // Raise alert (OPERATIONAL only)
290
- // await this.alertService.raiseDeviceOfflineAlert(device, source, reason);
291
312
  // Raise issue when the device goes offline if longer than the baseline (OPERATIONAL only)
292
313
  await this.issueService.createDeviceOfflineIssue(device, source, reason);
293
314
  }
@@ -310,9 +331,14 @@ let LocalDeviceService = (() => {
310
331
  newStatus.error = {}; // Clear the error
311
332
  await this.deviceRepository.setStatus(device.deviceId, newStatus);
312
333
  await this.eventHandler.onStatusChange(device.deviceId, newStatus, auditBody, constants_1.DT_EVENT_TYPES.DEVICE.STATUS.ONLINE);
313
- //TODO: ALERT NEEDED?
314
- // Raise alert
315
- // await this.alertService.raiseDeviceOnlineAlert(device, source, reason);
334
+ // Resolve issue if exists
335
+ await this.issueService.performIssueAction({
336
+ entityId: device.deviceId,
337
+ entityType: issue_types_1.EntityType.DEVICE,
338
+ type: issue_types_1.IssueType.DEVICE_OFFLINE,
339
+ propertyId: device.propertyId,
340
+ zoneId: device.zoneId,
341
+ }, "resolve", source);
316
342
  }
317
343
  }
318
344
  else if (isExistingStatusOffline) {
@@ -323,9 +349,14 @@ let LocalDeviceService = (() => {
323
349
  newStatus.error = undefined; // Clear the error
324
350
  await this.deviceRepository.setStatus(device.deviceId, newStatus);
325
351
  await this.eventHandler.onStatusChange(device.deviceId, newStatus, auditBody, constants_1.DT_EVENT_TYPES.DEVICE.STATUS.ONLINE);
326
- //TODO: ALERT NEEDED?
327
- // Raise alert
328
- // await this.alertService.raiseDeviceOnlineAlert(device, source, reason);
352
+ //Resolve issue if exists
353
+ await this.issueService.performIssueAction({
354
+ entityId: device.deviceId,
355
+ entityType: issue_types_1.EntityType.DEVICE,
356
+ type: issue_types_1.IssueType.DEVICE_OFFLINE,
357
+ propertyId: device.propertyId,
358
+ zoneId: device.zoneId,
359
+ }, "resolve", source);
329
360
  }
330
361
  }
331
362
  async getDeviceBaseline(deviceId) {
@@ -347,43 +378,40 @@ let LocalDeviceService = (() => {
347
378
  // Get device information
348
379
  const device = await this.getDevice(deviceId);
349
380
  // Fetch the old battery level state
350
- const oldBatteryLevel = device.state?.batteryPercentage;
381
+ const oldBatteryState = device.state?.batteryPercentage;
351
382
  // Check if battery level has changed
352
- const isDifferent = !(0, lodash_1.isEqual)(oldBatteryLevel?.value, batteryLevel);
383
+ const isDifferent = !(0, lodash_1.isEqual)(oldBatteryState?.value, batteryLevel);
353
384
  if (isDifferent) {
354
385
  // Check if current time is greater than or equal to last updated time (8-hour logic)
355
- const shouldUpdate = this.shouldUpdateBatteryLevel(oldBatteryLevel?.lastUpdated);
386
+ const shouldUpdate = this.shouldUpdateBatteryLevel(oldBatteryState?.lastUpdated);
356
387
  if (shouldUpdate) {
357
388
  // Save the battery level in the device
358
389
  await this.deviceRepository.setBatteryLevel(deviceId, batteryLevel);
359
390
  await this.eventHandler.onBatteryLevelChange(deviceId, batteryLevel, auditBody);
360
- if (oldBatteryLevel?.value < batteryLevel &&
361
- batteryLevel - oldBatteryLevel?.value > 10) {
362
- // Battery level has increased (considering as battery has been replaced)
363
- await this.eventHandler.onBatteryReplaced(deviceId, batteryLevel, auditBody);
364
- }
365
- // Additional condition: if battery level is less than 50, update battery state to "replace"
366
- if (batteryLevel < 50) {
391
+ if (oldBatteryState?.value < batteryLevel &&
392
+ batteryLevel - oldBatteryState?.value > 50) {
393
+ // Battery level has increased more than 50% (considering as battery has been replaced)
367
394
  const batteryState = {
368
395
  batteryState: {
369
- value: "replace",
396
+ value: "replaced",
370
397
  lastUpdated: new Date().toISOString(),
371
398
  },
372
399
  };
373
400
  await this.updateBatteryState(deviceId, batteryState);
374
- await this.eventHandler.onBatteryStateChange(deviceId, batteryState, auditBody);
401
+ await this.eventHandler.onBatteryReplaced(deviceId, batteryLevel, auditBody);
402
+ // Cancel any existing battery issue
403
+ await this.issueService.performIssueAction({
404
+ entityId: deviceId,
405
+ entityType: issue_types_1.EntityType.DEVICE,
406
+ type: issue_types_1.IssueType.BATTERY_LOW,
407
+ propertyId: device.propertyId,
408
+ zoneId: device.zoneId,
409
+ }, "resolve", source);
375
410
  }
376
411
  // Get property threshold
377
412
  const propertyThreshold = await this.getPropertyBatteryThreshold(device.propertyId);
378
413
  // Check if battery level is below threshold
379
414
  if (batteryLevel < propertyThreshold) {
380
- // Raise alert
381
- // await this.alertService.raiseDeviceBatteryAlert(
382
- // device,
383
- // batteryLevel,
384
- // propertyThreshold,
385
- // source
386
- // );
387
415
  // Raise issue when the level is below threshold
388
416
  await this.issueService.createDeviceBatteryIssue(device, batteryLevel, propertyThreshold, issue_types_1.IssuePriority.CRITICAL, source);
389
417
  }
@@ -415,37 +443,6 @@ let LocalDeviceService = (() => {
415
443
  }
416
444
  return preferences?.settings?.batteryPreference?.value ?? 20;
417
445
  }
418
- async checkForDeviceMalfunctions(device, source, reason) {
419
- // TODO: Implement device malfunction detection logic
420
- // This should check for:
421
- // - Lock jammed
422
- // - Device not accepting codes
423
- // - Other malfunction indicators
424
- // For now, we'll check if the reason indicates a malfunction
425
- const malfunctionIndicators = [
426
- "jammed",
427
- "not accepting codes",
428
- "malfunction",
429
- "error",
430
- "failure",
431
- ];
432
- const hasMalfunction = malfunctionIndicators.some((indicator) => reason?.toLowerCase().includes(indicator));
433
- // if (hasMalfunction) {
434
- // // Raise alert for device malfunction (READINESS + OPERATIONAL)
435
- // await this.alertService.raiseDeviceIssueAlert(
436
- // device,
437
- // "Device Malfunction Detected",
438
- // source,
439
- // reason
440
- // );
441
- // // Raise issue for device malfunction (READINESS + OPERATIONAL)
442
- // await this.issueService.createDeviceMalfunctionIssue(
443
- // device,
444
- // source,
445
- // reason
446
- // );
447
- // }
448
- }
449
446
  async getMetaData(deviceId) {
450
447
  if (!deviceId) {
451
448
  throw new Error("Device ID is required");
@@ -9,7 +9,6 @@ export declare class EventHandler {
9
9
  onStatusChange(deviceId: string, status: any, auditProperties: IAuditProperties, eventType: any): Promise<void>;
10
10
  onStatusChangeMany(query: IStatusQuery, status: IStatus, auditProperties: IAuditProperties, eventType: any): Promise<void>;
11
11
  onBatteryLevelChange(deviceId: string, batteryLevel: number, auditProperties: IAuditProperties): Promise<void>;
12
- onBatteryStateChange(deviceId: string, batteryState: any, auditProperties: IAuditProperties): Promise<void>;
13
12
  onBatteryReplaced(deviceId: string, batteryLevel: number, auditProperties: IAuditProperties): Promise<void>;
14
13
  onDeviceMetaChange(deviceId: string, metaData: Record<string, any>, auditProperties: IAuditProperties): Promise<void>;
15
14
  }
@@ -125,16 +125,6 @@ let EventHandler = (() => {
125
125
  },
126
126
  });
127
127
  }
128
- async onBatteryStateChange(deviceId, batteryState, auditProperties) {
129
- await (0, audit_1.pushAudit)({
130
- auditType: Event_1.DT_EVENT_TYPES.DEVICE.BATTERY.STATE.REPLACE,
131
- auditData: {
132
- deviceId,
133
- batteryState,
134
- ...auditProperties,
135
- },
136
- });
137
- }
138
128
  async onBatteryReplaced(deviceId, batteryLevel, auditProperties) {
139
129
  await (0, audit_1.pushAudit)({
140
130
  auditType: Event_1.DT_EVENT_TYPES.DEVICE.BATTERY.REPLACED,
@@ -1,5 +1,5 @@
1
1
  import { IIssueDocument } from "./Issue.model";
2
- import { CreateIssueData, UpdateIssueData, AddCommentData, IssuePriority, IssuesCategory, EntityType, IIssueQuery, IssueType } from "./issue.types";
2
+ import { CreateIssueData, UpdateIssueData, AddCommentData, IssuePriority, IssuesCategory, EntityType, EntitySubType, IIssueQuery, IssueType } from "./issue.types";
3
3
  import { IssueBuilder } from "./IssueBuilder";
4
4
  import { Source } from "../constants/Service";
5
5
  import { IDevice } from "../entities/device/local/interfaces";
@@ -47,10 +47,25 @@ export declare class IssueService {
47
47
  * Create issue for device battery level below threshold (READINESS + OPERATIONAL + ENERGY)
48
48
  */
49
49
  createDeviceBatteryIssue(device: IDevice, batteryLevel: number, threshold: number, priority: IssuePriority, source: Source): Promise<IIssueDocument | null>;
50
+ createAccountMissingDeviceIssue(device: {
51
+ deviceId: string;
52
+ deviceType: {
53
+ type: any;
54
+ };
55
+ propertyId: string;
56
+ name: string;
57
+ }, source: Source, reason?: string): Promise<IIssueDocument | null>;
58
+ createAccountUnauthorizedIssue(account: {
59
+ accountId: string;
60
+ accountType: EntitySubType;
61
+ propertyId: string;
62
+ name: string;
63
+ }, source: Source, reason?: string): Promise<IIssueDocument | null>;
50
64
  /**
51
65
  * Create issue for device malfunction (jammed or not accepting codes) (READINESS + OPERATIONAL)
52
66
  */
53
67
  createDeviceMalfunctionIssue(device: IDevice, source: Source, reason?: string): Promise<IIssueDocument | null>;
68
+ createLockJammedIssue(device: IDevice, source: Source): Promise<IIssueDocument | null>;
54
69
  /**
55
70
  * Create a maintenance issue using IssueBuilder
56
71
  */
@@ -113,7 +128,7 @@ export declare class IssueService {
113
128
  */
114
129
  unassignIssue(id: string, unassignedBy: string): Promise<IIssueDocument | null>;
115
130
  /**
116
- * Cancel/Close/Resolve an issue based on query
131
+ * Cancel/Close/Resolve/Ignore/In_Progress/On_Hold an issue based on query
117
132
  * This method will find an issue matching the query and update its status
118
133
  */
119
134
  performIssueAction(query: IIssueQuery, action: "cancel" | "close" | "resolve" | "ignore" | "in_progress" | "on_hold", updatedBy: string): Promise<IIssueDocument | null>;
@@ -243,38 +243,18 @@ let IssueService = (() => {
243
243
  entitySubType: device.deviceType.type,
244
244
  propertyId: device.propertyId,
245
245
  zoneId: device.zoneId,
246
- title: "Device Offline - Requires Attention",
246
+ title: device.deviceType.type.toLowerCase() === "hub"
247
+ ? "Hub Offline"
248
+ : "Device Offline",
247
249
  description: `${device.name} has been offline for longer than the threshold time. ${reason ? `Reason: ${reason}.` : ""}`,
248
250
  createdBy: source,
249
251
  category: issue_types_1.IssuesCategory.OPERATIONS,
250
252
  priority: issue_types_1.IssuePriority.CRITICAL,
251
- type: issue_types_1.IssueType.DEVICE_OFFLINE,
253
+ type: device.deviceType.type.toLowerCase() === "hub"
254
+ ? issue_types_1.IssueType.HUB_OFFLINE
255
+ : issue_types_1.IssueType.DEVICE_OFFLINE,
252
256
  });
253
257
  }
254
- // async createDoorLeftOpenIssue(
255
- // device: IDevice,
256
- // source: Source,
257
- // reason?: string
258
- // ): Promise<IIssueDocument | null> {
259
- // const zone = await Container.get(AdminService).getZone(device.zoneId);
260
- // return await this.createDeviceIssue({
261
- // entityId: device.deviceId,
262
- // entityType: EntityType.DEVICE,
263
- // entitySubType: device.deviceType.type as EntitySubType,
264
- // propertyId: device.propertyId,
265
- // zoneId: device.zoneId,
266
- // title: "Door Left Open - Requires Attention",
267
- // description: `${
268
- // zone?.name
269
- // } has a door left open, for more than 10 minutes. ${
270
- // reason ? `Reason: ${reason}.` : ""
271
- // }`,
272
- // createdBy: source,
273
- // category: IssuesCategory.SECURITY,
274
- // priority: IssuePriority.HIGH,
275
- // type: IssueType.DOOR_LEFT_OPEN,
276
- // });
277
- // }
278
258
  /**
279
259
  * Create issue for device battery level below threshold (READINESS + OPERATIONAL + ENERGY)
280
260
  */
@@ -285,7 +265,7 @@ let IssueService = (() => {
285
265
  entitySubType: device.deviceType.type,
286
266
  propertyId: device.propertyId,
287
267
  zoneId: device.zoneId,
288
- title: "Device Battery Low - Requires Attention",
268
+ title: "Device Battery Low",
289
269
  description: `${device.name} battery level is ${batteryLevel}%, which is below the property threshold of ${threshold}%.`,
290
270
  createdBy: source,
291
271
  category: issue_types_1.IssuesCategory.ENERGY,
@@ -293,6 +273,34 @@ let IssueService = (() => {
293
273
  type: issue_types_1.IssueType.BATTERY_LOW,
294
274
  });
295
275
  }
276
+ async createAccountMissingDeviceIssue(device, source, reason) {
277
+ return await this.createIssue({
278
+ entityId: device.deviceId,
279
+ entityType: issue_types_1.EntityType.DEVICE,
280
+ entitySubType: device.deviceType.type,
281
+ propertyId: device.propertyId,
282
+ title: "Device Missing",
283
+ description: `${device.name} is missing from the account. ${reason ? `Reason: ${reason}.` : ""}`,
284
+ createdBy: source,
285
+ category: issue_types_1.IssuesCategory.OPERATIONS,
286
+ priority: issue_types_1.IssuePriority.CRITICAL,
287
+ type: issue_types_1.IssueType.ACCOUNT_MISSING_DEVICE,
288
+ });
289
+ }
290
+ async createAccountUnauthorizedIssue(account, source, reason) {
291
+ return await this.createIssue({
292
+ entityId: account.accountId,
293
+ entityType: issue_types_1.EntityType.CLOUD_DEVICE_ACCOUNT,
294
+ entitySubType: account.accountType,
295
+ propertyId: account.propertyId,
296
+ title: "Account re-authorization required",
297
+ description: `${account.name}'s authorization has expired. Please re-authorize the account. ${reason ? `Reason: ${reason}.` : ""}`,
298
+ createdBy: source,
299
+ category: issue_types_1.IssuesCategory.OPERATIONS,
300
+ priority: issue_types_1.IssuePriority.CRITICAL,
301
+ type: issue_types_1.IssueType.ACCOUNT_UNAUTHORIZED,
302
+ });
303
+ }
296
304
  /**
297
305
  * Create issue for device malfunction (jammed or not accepting codes) (READINESS + OPERATIONAL)
298
306
  */
@@ -303,7 +311,7 @@ let IssueService = (() => {
303
311
  entitySubType: device.deviceType.type,
304
312
  propertyId: device.propertyId,
305
313
  zoneId: device.zoneId,
306
- title: `Device Malfunction - Requires Attention`,
314
+ title: `Device Malfunction`,
307
315
  description: `${device.name} is malfunctioning. ${reason ? `Reason: ${reason}.` : ""}`,
308
316
  createdBy: source,
309
317
  category: issue_types_1.IssuesCategory.OPERATIONS,
@@ -311,6 +319,21 @@ let IssueService = (() => {
311
319
  type: issue_types_1.IssueType.DEVICE_MALFUNCTION,
312
320
  });
313
321
  }
322
+ async createLockJammedIssue(device, source) {
323
+ return await this.createDeviceIssue({
324
+ entityId: device.deviceId,
325
+ entityType: issue_types_1.EntityType.DEVICE,
326
+ entitySubType: device.deviceType.type,
327
+ propertyId: device.propertyId,
328
+ zoneId: device.zoneId,
329
+ title: "Lock Jammed",
330
+ description: `${device.name} is jammed. Requires Immediate Attention.`,
331
+ createdBy: source,
332
+ category: issue_types_1.IssuesCategory.OPERATIONS,
333
+ priority: issue_types_1.IssuePriority.HIGH,
334
+ type: issue_types_1.IssueType.LOCK_JAMMED,
335
+ });
336
+ }
314
337
  /**
315
338
  * Create a maintenance issue using IssueBuilder
316
339
  */
@@ -647,7 +670,7 @@ let IssueService = (() => {
647
670
  return await issueModel.save();
648
671
  }
649
672
  /**
650
- * Cancel/Close/Resolve an issue based on query
673
+ * Cancel/Close/Resolve/Ignore/In_Progress/On_Hold an issue based on query
651
674
  * This method will find an issue matching the query and update its status
652
675
  */
653
676
  async performIssueAction(query, action, updatedBy) {
@@ -913,10 +936,11 @@ let IssueService = (() => {
913
936
  applyBusinessRules(filters) {
914
937
  // Business logic: Apply additional filters based on business rules
915
938
  const enhancedFilters = { ...filters };
916
- // Example: Always exclude cancelled issues unless explicitly requested
917
- if (!enhancedFilters.status ||
918
- enhancedFilters.status !== issue_types_1.IssueStatus.CANCELLED) {
919
- enhancedFilters.status = { $ne: issue_types_1.IssueStatus.CANCELLED };
939
+ // Always exclude cancelled, resolved, and closed issues unless explicitly requested
940
+ if (!enhancedFilters.status) {
941
+ enhancedFilters.status = {
942
+ $nin: [issue_types_1.IssueStatus.CANCELLED, issue_types_1.IssueStatus.RESOLVED, issue_types_1.IssueStatus.CLOSED],
943
+ };
920
944
  }
921
945
  return enhancedFilters;
922
946
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-common-device",
3
- "version": "7.4.0",
3
+ "version": "7.6.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [