dt-common-device 3.0.2 → 3.0.4

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.
Files changed (80) hide show
  1. package/README.md +7 -2
  2. package/dist/alerts/Alert.model.js +8 -2
  3. package/dist/alerts/Alert.repository.d.ts +3 -3
  4. package/dist/alerts/Alert.repository.js +28 -5
  5. package/dist/alerts/Alert.service.d.ts +18 -4
  6. package/dist/alerts/Alert.service.js +34 -3
  7. package/dist/alerts/AlertBuilder.d.ts +1 -1
  8. package/dist/alerts/AlertBuilder.js +1 -1
  9. package/dist/alerts/AlertService.example.js +5 -3
  10. package/dist/alerts/alert.types.d.ts +3 -3
  11. package/dist/config/config.d.ts +1 -1
  12. package/dist/config/config.js +3 -3
  13. package/dist/connection/Connection.repository.js +1 -1
  14. package/dist/constants/ConnectionProviders.d.ts +11 -0
  15. package/dist/constants/ConnectionProviders.js +14 -0
  16. package/dist/constants/Event.d.ts +14 -0
  17. package/dist/constants/Event.js +14 -0
  18. package/dist/constants/Service.d.ts +15 -0
  19. package/dist/constants/Service.js +19 -0
  20. package/dist/constants/index.d.ts +3 -0
  21. package/dist/constants/index.js +19 -0
  22. package/dist/db/redis.js +1 -0
  23. package/dist/device/cloud/entities/CloudDevice.d.ts +2 -2
  24. package/dist/device/cloud/entities/CloudDeviceService.d.ts +1 -1
  25. package/dist/device/cloud/entities/DeviceFactory.d.ts +1 -1
  26. package/dist/device/cloud/entities/DeviceFactory.js +1 -1
  27. package/dist/device/local/interfaces/IDevice.d.ts +3 -2
  28. package/dist/device/local/repository/Device.repository.js +3 -3
  29. package/dist/device/local/repository/Hub.repository.js +3 -3
  30. package/dist/device/local/repository/Schedule.repository.js +2 -2
  31. package/dist/device/local/services/Device.service.d.ts +11 -2
  32. package/dist/device/local/services/Device.service.js +145 -25
  33. package/dist/events/BaseEventHandler.d.ts +2 -2
  34. package/dist/events/BaseEventHandler.js +2 -2
  35. package/dist/events/BaseEventTransformer.d.ts +1 -1
  36. package/dist/events/BaseEventTransformer.js +1 -1
  37. package/dist/events/DeviceEventHandler.d.ts +1 -1
  38. package/dist/events/DeviceEventHandler.js +4 -3
  39. package/dist/events/EventHandler.js +1 -1
  40. package/dist/events/EventHandlerOrchestrator.js +1 -1
  41. package/dist/events/EventProcessingService.js +1 -1
  42. package/dist/events/InternalEventSubscription.js +1 -1
  43. package/dist/index.d.ts +1 -0
  44. package/dist/index.js +2 -0
  45. package/dist/issues/Issue.service.d.ts +11 -1
  46. package/dist/issues/Issue.service.js +16 -3
  47. package/dist/issues/IssueService.example.js +4 -2
  48. package/package.json +1 -1
  49. package/src/alerts/Alert.model.ts +8 -2
  50. package/src/alerts/Alert.repository.ts +28 -6
  51. package/src/alerts/Alert.service.ts +91 -7
  52. package/src/alerts/AlertBuilder.ts +2 -2
  53. package/src/alerts/AlertService.example.ts +5 -3
  54. package/src/alerts/alert.types.ts +3 -3
  55. package/src/config/config.ts +3 -3
  56. package/src/connection/Connection.repository.ts +1 -1
  57. package/src/constants/ConnectionProviders.ts +11 -0
  58. package/src/constants/Event.ts +14 -0
  59. package/src/constants/Service.ts +17 -0
  60. package/src/constants/index.ts +3 -0
  61. package/src/db/redis.ts +1 -0
  62. package/src/device/cloud/entities/CloudDevice.ts +2 -2
  63. package/src/device/cloud/entities/CloudDeviceService.ts +1 -1
  64. package/src/device/cloud/entities/DeviceFactory.ts +2 -2
  65. package/src/device/local/interfaces/IDevice.ts +3 -2
  66. package/src/device/local/repository/Device.repository.ts +3 -3
  67. package/src/device/local/repository/Hub.repository.ts +3 -3
  68. package/src/device/local/repository/Schedule.repository.ts +2 -2
  69. package/src/device/local/services/Device.service.ts +231 -29
  70. package/src/events/BaseEventHandler.ts +3 -3
  71. package/src/events/BaseEventTransformer.ts +2 -2
  72. package/src/events/DeviceEventHandler.ts +6 -4
  73. package/src/events/EventHandler.ts +1 -1
  74. package/src/events/EventHandlerOrchestrator.ts +2 -2
  75. package/src/events/EventProcessingService.ts +2 -2
  76. package/src/events/InternalEventSubscription.ts +2 -2
  77. package/src/index.ts +3 -0
  78. package/src/issues/Issue.service.ts +67 -7
  79. package/src/issues/IssueService.example.ts +4 -2
  80. package/tsconfig.json +0 -4
@@ -4,14 +4,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.LocalDeviceService = void 0;
7
- const EventHandler_1 = require("src/events/EventHandler");
7
+ const EventHandler_1 = require("../../../events/EventHandler");
8
8
  const lodash_1 = require("lodash");
9
9
  const Device_repository_1 = require("../repository/Device.repository");
10
10
  const typedi_1 = __importDefault(require("typedi"));
11
+ const Alert_service_1 = require("../../../alerts/Alert.service");
12
+ const Issue_service_1 = require("../../../issues/Issue.service");
13
+ const config_1 = require("../../../config/config");
11
14
  class LocalDeviceService {
12
15
  constructor() {
13
16
  this.eventHandler = new EventHandler_1.EventHandler();
14
17
  this.deviceRepository = typedi_1.default.get(Device_repository_1.DeviceRepository);
18
+ this.alertService = typedi_1.default.get(Alert_service_1.AlertService);
19
+ this.issueService = typedi_1.default.get(Issue_service_1.IssueService);
20
+ this.logger = (0, config_1.getConfig)().LOGGER;
15
21
  }
16
22
  async createDevice(body) {
17
23
  const device = await this.deviceRepository.createDevice(body);
@@ -80,50 +86,164 @@ class LocalDeviceService {
80
86
  }
81
87
  return await this.deviceRepository.getStatus(deviceId);
82
88
  }
83
- async setStatus(deviceId, newStatus) {
89
+ async setStatus(deviceId, newStatus, source, reason) {
84
90
  if (!deviceId || !newStatus) {
85
91
  throw new Error("Device ID and new status are required");
86
92
  }
87
- const oldStatus = await this.getStatus(deviceId);
88
- const isStatusChanged = !(0, lodash_1.isEqual)(oldStatus, newStatus);
89
- if (isStatusChanged) {
93
+ const device = await this.getDevice(deviceId);
94
+ const oldStatus = device.status;
95
+ const currentTime = new Date().toISOString();
96
+ // Determine if the new status is ONLINE or OFFLINE
97
+ const isNewStatusOnline = newStatus.liveStatus === "ONLINE";
98
+ const isNewStatusOffline = newStatus.liveStatus === "OFFLINE";
99
+ if (isNewStatusOffline) {
100
+ // New Status = OFFLINE
101
+ await this.handleOfflineStatus(deviceId, device, oldStatus, newStatus, source, reason, currentTime);
102
+ }
103
+ else if (isNewStatusOnline) {
104
+ // New Status = ONLINE
105
+ await this.handleOnlineStatus(deviceId, device, oldStatus, newStatus, source, reason, currentTime);
106
+ }
107
+ else {
108
+ // For any other status, just update normally
109
+ await this.deviceRepository.setStatus(deviceId, newStatus);
110
+ await this.eventHandler.onStatusChange(deviceId, newStatus);
111
+ }
112
+ }
113
+ async handleOfflineStatus(deviceId, device, oldStatus, newStatus, source, reason, currentTime) {
114
+ const isExistingStatusOnline = oldStatus?.online === true;
115
+ const isExistingStatusOffline = oldStatus?.online === false;
116
+ if (isExistingStatusOnline) {
117
+ // Existing status is Online
118
+ newStatus.online = true;
119
+ newStatus.liveStatus = "OFFLINE";
120
+ newStatus.lastUpdated = currentTime;
121
+ newStatus.error = {
122
+ type: "offline",
123
+ message: reason || "Device went offline",
124
+ default: {},
125
+ };
90
126
  await this.deviceRepository.setStatus(deviceId, newStatus);
91
127
  await this.eventHandler.onStatusChange(deviceId, newStatus);
92
128
  }
93
- const isBothOffline = oldStatus?.online === false && newStatus?.online === false;
94
- if (isBothOffline && oldStatus?.lastUpdated) {
95
- const timeDifference = Date.now() - new Date(oldStatus.lastUpdated).getTime();
96
- if (timeDifference > 86400000) {
97
- //return await this.alertService.raiseOperationsAlert();
129
+ else if (isExistingStatusOffline) {
130
+ // Existing status is Offline
131
+ const timeLapsed = oldStatus?.lastUpdated
132
+ ? Date.now() - new Date(oldStatus.lastUpdated).getTime()
133
+ : 0;
134
+ const baselineTime = await this.getDeviceBaseline(deviceId);
135
+ if (timeLapsed > baselineTime) {
136
+ // When the time lapsed is higher than the baseline time
137
+ newStatus.online = false;
138
+ newStatus.liveStatus = "OFFLINE";
139
+ newStatus.lastUpdated = currentTime;
140
+ newStatus.error = {
141
+ type: "offline",
142
+ message: reason || "Device has been offline for longer than baseline",
143
+ default: {},
144
+ };
145
+ await this.deviceRepository.setStatus(deviceId, newStatus);
146
+ await this.eventHandler.onStatusChange(deviceId, newStatus);
147
+ // Raise alert
148
+ await this.alertService.raiseDeviceOfflineAlert(device, source, reason);
149
+ // Raise issue when the device goes offline if longer than the baseline
150
+ await this.issueService.createDeviceOfflineIssue(device, source, reason);
151
+ }
152
+ }
153
+ }
154
+ async handleOnlineStatus(deviceId, device, oldStatus, newStatus, source, reason, currentTime) {
155
+ const isExistingStatusOnline = oldStatus?.online === true;
156
+ const isExistingStatusOffline = oldStatus?.online === false;
157
+ if (isExistingStatusOnline) {
158
+ // Existing status is Online
159
+ if (oldStatus?.liveStatus === "ONLINE") {
160
+ // liveStatus = Online → Do nothing
98
161
  return;
99
162
  }
163
+ else if (oldStatus?.liveStatus === "OFFLINE") {
164
+ // liveStatus = Offline → Follow the step #2 (same as existing status is Offline)
165
+ newStatus.online = true;
166
+ newStatus.liveStatus = "ONLINE";
167
+ newStatus.lastUpdated = currentTime;
168
+ newStatus.error = {}; // Clear the error
169
+ await this.deviceRepository.setStatus(deviceId, newStatus);
170
+ await this.eventHandler.onStatusChange(deviceId, newStatus);
171
+ //TODO: ALERT NEEDED?
172
+ // Raise alert
173
+ await this.alertService.raiseDeviceOnlineAlert(device, source, reason);
174
+ }
175
+ }
176
+ else if (isExistingStatusOffline) {
177
+ // Existing status is Offline
178
+ newStatus.online = true;
179
+ newStatus.liveStatus = "ONLINE";
180
+ newStatus.lastUpdated = currentTime;
181
+ newStatus.error = undefined; // Clear the error
182
+ await this.deviceRepository.setStatus(deviceId, newStatus);
183
+ await this.eventHandler.onStatusChange(deviceId, newStatus);
184
+ //TODO: ALERT NEEDED?
185
+ // Raise alert
186
+ await this.alertService.raiseDeviceOnlineAlert(device, source, reason);
100
187
  }
101
188
  }
189
+ async getDeviceBaseline(deviceId) {
190
+ // TODO: Implement device baseline retrieval
191
+ // This should return the baseline time in milliseconds for the specific device
192
+ // For now, returning a default value of 5 minutes (300000ms)
193
+ // In a real implementation, this would fetch from device configuration or settings
194
+ return 3600000; // 1 hour in milliseconds
195
+ }
102
196
  async getBatteryLevel(deviceId) {
103
197
  if (!deviceId) {
104
198
  throw new Error("Device ID is required");
105
199
  }
106
200
  return await this.deviceRepository.getBatteryLevel(deviceId);
107
201
  }
108
- async setBatteryLevel(deviceId, batteryLevel) {
202
+ async setBatteryLevel(deviceId, batteryLevel, source) {
203
+ if (!deviceId || batteryLevel === undefined || batteryLevel === null) {
204
+ throw new Error("Device ID and battery level are required");
205
+ }
206
+ // Get device information
207
+ const device = await this.getDevice(deviceId);
109
208
  // Fetch the old battery level state
110
- const oldBatteryLevel = await this.getBatteryLevel(deviceId);
209
+ const oldBatteryLevel = device.state?.batteryPercentage;
210
+ // Check if battery level has changed
111
211
  const isDifferent = !(0, lodash_1.isEqual)(oldBatteryLevel?.value, batteryLevel);
112
212
  if (isDifferent) {
113
- await this.deviceRepository.setBatteryLevel(deviceId, batteryLevel);
114
- await this.eventHandler.onBatteryLevelChange(deviceId, batteryLevel);
115
- }
116
- // Check if battery is low and not updated for 8 hours
117
- if (batteryLevel < 20 &&
118
- oldBatteryLevel?.value < 20 &&
119
- oldBatteryLevel?.lastUpdated &&
120
- new Date(oldBatteryLevel.lastUpdated).getTime() <
121
- Date.now() - 1000 * 60 * 60 * 8 // 8 hours in ms
122
- ) {
123
- // TODO: Raise an energy alert
124
- // await this.alertService.raiseEnergyAlert();
125
- return;
213
+ // Check if current time is greater than or equal to last updated time (8-hour logic)
214
+ const shouldUpdate = this.shouldUpdateBatteryLevel(oldBatteryLevel?.lastUpdated);
215
+ if (shouldUpdate) {
216
+ // Save the battery level in the device
217
+ await this.deviceRepository.setBatteryLevel(deviceId, batteryLevel);
218
+ await this.eventHandler.onBatteryLevelChange(deviceId, batteryLevel);
219
+ // Get property threshold
220
+ const propertyThreshold = await this.getPropertyBatteryThreshold(device.propertyId);
221
+ // Check if battery level is below threshold
222
+ if (batteryLevel < propertyThreshold) {
223
+ // Raise alert
224
+ await this.alertService.raiseDeviceBatteryAlert(device, batteryLevel, propertyThreshold, source);
225
+ // Raise issue when the level is below threshold
226
+ await this.issueService.createDeviceBatteryIssue(device, batteryLevel, propertyThreshold, source);
227
+ }
228
+ }
229
+ }
230
+ }
231
+ shouldUpdateBatteryLevel(lastUpdated) {
232
+ if (!lastUpdated) {
233
+ return true; // No previous update, so we should update
126
234
  }
235
+ const lastUpdateTime = new Date(lastUpdated).getTime();
236
+ const currentTime = Date.now();
237
+ const eightHoursInMs = 8 * 60 * 60 * 1000; // 8 hours in milliseconds
238
+ // Return true if current time is greater than or equal to last updated time + 8 hours
239
+ return currentTime >= lastUpdateTime + eightHoursInMs;
240
+ }
241
+ async getPropertyBatteryThreshold(propertyId) {
242
+ // TODO: Implement property battery threshold retrieval
243
+ // This should return the battery threshold for the specific property
244
+ // For now, returning a default value of 20%
245
+ // In a real implementation, this would fetch from property configuration or settings
246
+ return 20; // 20% default threshold
127
247
  }
128
248
  async getMetaData(deviceId) {
129
249
  if (!deviceId) {
@@ -1,7 +1,7 @@
1
1
  import { DeviceEvent } from "./interfaces/DeviceEvent";
2
2
  import { IEventHandler } from "./interfaces/IEventHandler";
3
- import { ILogger } from "src/config/config.types";
4
- import { LocalDeviceService } from "src/device/local/services/Device.service";
3
+ import { ILogger } from "../config/config.types";
4
+ import { LocalDeviceService } from "../device/local/services/Device.service";
5
5
  export declare abstract class BaseEventHandler implements IEventHandler {
6
6
  protected readonly supportedEventTypes: string[];
7
7
  protected readonly priority: number;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseEventHandler = void 0;
4
- const config_1 = require("src/config/config");
5
- const Device_service_1 = require("src/device/local/services/Device.service");
4
+ const config_1 = require("../config/config");
5
+ const Device_service_1 = require("../device/local/services/Device.service");
6
6
  class BaseEventHandler {
7
7
  constructor(supportedEventTypes, priority = 100) {
8
8
  this.supportedEventTypes = supportedEventTypes;
@@ -1,4 +1,4 @@
1
- import { ILogger } from "src/config/config.types";
1
+ import { ILogger } from "../config/config.types";
2
2
  import { DeviceEvent } from "./interfaces/DeviceEvent";
3
3
  import { IEventTransformer } from "./interfaces/IEventTransformer";
4
4
  export declare abstract class BaseEventTransformer implements IEventTransformer {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseEventTransformer = void 0;
4
- const config_1 = require("src/config/config");
4
+ const config_1 = require("../config/config");
5
5
  class BaseEventTransformer {
6
6
  constructor(deviceType, rawData) {
7
7
  this.deviceType = deviceType;
@@ -1,4 +1,4 @@
1
- import { IDevice } from "src/device/local/interfaces";
1
+ import { IDevice } from "../device/local/interfaces";
2
2
  import { BaseEventHandler } from "./BaseEventHandler";
3
3
  import { DeviceEvent, DeviceEventEntity, EventConstructionOptions } from "./interfaces/DeviceEvent";
4
4
  export declare class DeviceEventHandler extends BaseEventHandler {
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeviceEventHandler = void 0;
4
- const Event_1 = require("src/constants/Event");
5
- const services_1 = require("src/device/local/services");
4
+ const constants_1 = require("../constants");
5
+ const Event_1 = require("../constants/Event");
6
+ const services_1 = require("../device/local/services");
6
7
  const BaseEventHandler_1 = require("./BaseEventHandler");
7
8
  class DeviceEventHandler extends BaseEventHandler_1.BaseEventHandler {
8
9
  constructor() {
@@ -57,7 +58,7 @@ class DeviceEventHandler extends BaseEventHandler_1.BaseEventHandler {
57
58
  });
58
59
  break;
59
60
  case Event_1.DT_EVENT_TYPES.DEVICE.BATTERY.CHANGED:
60
- await this.localDeviceService.setBatteryLevel(deviceData.deviceId, event?.data?.batteryLevel ?? 0);
61
+ await this.localDeviceService.setBatteryLevel(deviceData.deviceId, event?.data?.batteryLevel ?? 0, constants_1.Source.CLOUD_EVENT);
61
62
  break;
62
63
  }
63
64
  }
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EventHandler = void 0;
4
4
  const dt_pub_sub_1 = require("dt-pub-sub");
5
5
  const dt_audit_library_1 = require("dt-audit-library");
6
- const Event_1 = require("src/constants/Event");
6
+ const Event_1 = require("../constants/Event");
7
7
  class EventHandler {
8
8
  constructor() {
9
9
  this.source = "dt-common-device";
@@ -40,7 +40,7 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.EventHandlerOrchestrator = void 0;
42
42
  const typedi_1 = require("typedi");
43
- const config_1 = require("src/config/config");
43
+ const config_1 = require("../config/config");
44
44
  let EventHandlerOrchestrator = (() => {
45
45
  let _classDecorators = [(0, typedi_1.Service)()];
46
46
  let _classDescriptor;
@@ -41,7 +41,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.EventProcessingService = void 0;
42
42
  const typedi_1 = require("typedi");
43
43
  const DeviceEventHandler_1 = require("./DeviceEventHandler");
44
- const config_1 = require("src/config/config");
44
+ const config_1 = require("../config/config");
45
45
  const EventHandlerOrchestrator_1 = require("./EventHandlerOrchestrator");
46
46
  let EventProcessingService = (() => {
47
47
  let _classDecorators = [(0, typedi_1.Service)()];
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InternalEventSubscription = exports.InternalEventType = void 0;
4
4
  const dt_pub_sub_1 = require("dt-pub-sub");
5
- const config_1 = require("src/config/config");
5
+ const config_1 = require("../config/config");
6
6
  // Event types enum for better type safety
7
7
  var InternalEventType;
8
8
  (function (InternalEventType) {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./constants";
1
2
  export { CloudDevice, CloudDeviceService, DeviceFactory, } from "./device/cloud/entities";
2
3
  export { LocalDeviceService, LocalHubService, LocalScheduleService, } from "./device/local/services";
3
4
  export * from "./device/local/interfaces";
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.shutdown = exports.getConfig = exports.initialize = exports.LocalScheduleService = exports.LocalHubService = exports.LocalDeviceService = exports.DeviceFactory = exports.CloudDeviceService = exports.CloudDevice = void 0;
19
+ // CONSTANTS EXPORTS
20
+ __exportStar(require("./constants"), exports);
19
21
  // DEVICE EXPORTS
20
22
  var entities_1 = require("./device/cloud/entities");
21
23
  Object.defineProperty(exports, "CloudDevice", { enumerable: true, get: function () { return entities_1.CloudDevice; } });
@@ -2,6 +2,8 @@ import { IssueRepository } from "./Issue.repository";
2
2
  import { IIssueDocument } from "./Issue.model";
3
3
  import { CreateIssueData, UpdateIssueData, AddCommentData, IssueStatus, IssuePriority, IssuesCategory, EntityType } from "./issue.types";
4
4
  import { IssueBuilder } from "./IssueBuilder";
5
+ import { Source } from "../constants/Service";
6
+ import { IDevice } from "../device/local/interfaces";
5
7
  export declare class IssueService {
6
8
  private readonly issueRepository;
7
9
  constructor(issueRepository: IssueRepository);
@@ -24,7 +26,7 @@ export declare class IssueService {
24
26
  /**
25
27
  * Create a device-specific issue using IssueBuilder
26
28
  */
27
- createDeviceIssue(deviceId: string, propertyId: string, title: string, description: string, createdBy: string, category?: IssuesCategory, priority?: IssuePriority, assignedTo?: string, dueDate?: Date): Promise<IIssueDocument>;
29
+ createDeviceIssue(deviceId: string, propertyId: string, title: string, description: string, source: Source, category?: IssuesCategory, priority?: IssuePriority, assignedTo?: string, dueDate?: Date): Promise<IIssueDocument>;
28
30
  /**
29
31
  * Create a hub-specific issue using IssueBuilder
30
32
  */
@@ -33,6 +35,14 @@ export declare class IssueService {
33
35
  * Create a user-specific issue using IssueBuilder
34
36
  */
35
37
  createUserIssue(userId: string, propertyId: string, title: string, description: string, createdBy: string, category?: IssuesCategory, priority?: IssuePriority, assignedTo?: string, dueDate?: Date): Promise<IIssueDocument>;
38
+ /**
39
+ * Create issue for device going offline longer than baseline
40
+ */
41
+ createDeviceOfflineIssue(device: IDevice, source: Source, reason?: string): Promise<IIssueDocument>;
42
+ /**
43
+ * Create issue for device battery level below threshold
44
+ */
45
+ createDeviceBatteryIssue(device: IDevice, batteryLevel: number, threshold: number, source: Source): Promise<IIssueDocument>;
36
46
  /**
37
47
  * Create a maintenance issue using IssueBuilder
38
48
  */
@@ -131,11 +131,11 @@ let IssueService = (() => {
131
131
  /**
132
132
  * Create a device-specific issue using IssueBuilder
133
133
  */
134
- async createDeviceIssue(deviceId, propertyId, title, description, createdBy, category, priority, assignedTo, dueDate) {
134
+ async createDeviceIssue(deviceId, propertyId, title, description, source, category, priority, assignedTo, dueDate) {
135
135
  const issueBuilder = IssueBuilder_1.IssueBuilder.createDeviceIssue(deviceId, propertyId)
136
136
  .setTitle(title)
137
137
  .setDescription(description)
138
- .setCreatedBy(createdBy);
138
+ .setCreatedBy(source);
139
139
  if (category)
140
140
  issueBuilder.setCategory(category);
141
141
  if (priority)
@@ -182,6 +182,18 @@ let IssueService = (() => {
182
182
  issueBuilder.setDueDate(dueDate);
183
183
  return await this.createIssue(issueBuilder);
184
184
  }
185
+ /**
186
+ * Create issue for device going offline longer than baseline
187
+ */
188
+ async createDeviceOfflineIssue(device, source, reason) {
189
+ return await this.createDeviceIssue(device.deviceId, device.propertyId, "Device Offline - Requires Attention", `Device ${device.name} (${device.deviceId}) has been offline for longer than the baseline time. ${reason ? `Reason: ${reason}` : ""} This requires immediate attention to restore device functionality.`, source, issue_types_1.IssuesCategory.OPERATIONS, issue_types_1.IssuePriority.HIGH);
190
+ }
191
+ /**
192
+ * Create issue for device battery level below threshold
193
+ */
194
+ async createDeviceBatteryIssue(device, batteryLevel, threshold, source) {
195
+ return await this.createDeviceIssue(device.deviceId, device.propertyId, "Device Battery Low - Requires Attention", `Device ${device.name} (${device.deviceId}) battery level is ${batteryLevel}%, which is below the property threshold of ${threshold}%. This requires immediate attention to replace or charge the device battery.`, source, issue_types_1.IssuesCategory.ENERGY, issue_types_1.IssuePriority.MEDIUM);
196
+ }
185
197
  /**
186
198
  * Create a maintenance issue using IssueBuilder
187
199
  */
@@ -230,7 +242,8 @@ let IssueService = (() => {
230
242
  processedIssueData.priority = this.determineDefaultPriority(processedIssueData.category);
231
243
  }
232
244
  // Business logic: Validate due date is in the future
233
- if (processedIssueData.dueDate && processedIssueData.dueDate <= new Date()) {
245
+ if (processedIssueData.dueDate &&
246
+ processedIssueData.dueDate <= new Date()) {
234
247
  throw new Error("Due date must be in the future");
235
248
  }
236
249
  return await this.issueRepository.create(processedIssueData);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.IssueServiceExample = void 0;
4
+ const constants_1 = require("../constants");
4
5
  const IssueBuilder_1 = require("./IssueBuilder");
5
6
  const issue_types_1 = require("./issue.types");
6
7
  /**
@@ -49,11 +50,11 @@ class IssueServiceExample {
49
50
  */
50
51
  async createDeviceIssues() {
51
52
  // Create a device issue with default category and priority
52
- const deviceIssue1 = await this.issueService.createDeviceIssue("device123", "prop456", "Device Battery Low", "Device battery level is below 20%", "system", undefined, // Use default category
53
+ const deviceIssue1 = await this.issueService.createDeviceIssue("device123", "prop456", "Device Battery Low", "Device battery level is below 20%", constants_1.Source.CLOUD_EVENT, undefined, // Use default category
53
54
  undefined, // Use default priority
54
55
  "maintenance-team", new Date("2024-01-12"));
55
56
  // Create a device issue with custom category and priority
56
- const deviceIssue2 = await this.issueService.createDeviceIssue("device789", "prop456", "Device Firmware Update Available", "New firmware version is available for installation", "firmware-manager", issue_types_1.IssuesCategory.READINESS, issue_types_1.IssuePriority.MEDIUM, "admin", new Date("2024-01-20"));
57
+ const deviceIssue2 = await this.issueService.createDeviceIssue("device789", "prop456", "Device Firmware Update Available", "New firmware version is available for installation", constants_1.Source.CLOUD_EVENT, issue_types_1.IssuesCategory.READINESS, issue_types_1.IssuePriority.MEDIUM, "admin", new Date("2024-01-20"));
57
58
  return { deviceIssue1, deviceIssue2 };
58
59
  }
59
60
  /**
@@ -168,6 +169,7 @@ class IssueServiceExample {
168
169
  assignedTo: "tech-support",
169
170
  createdBy: "legacy-system",
170
171
  dueDate: new Date("2024-01-20"),
172
+ source: constants_1.Source.CLOUD_EVENT,
171
173
  };
172
174
  // This still works with the updated createIssue method
173
175
  const issue = await this.issueService.createIssue(issueData);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dt-common-device",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {
@@ -47,9 +47,15 @@ interface IAlertModel extends Model<IAlertDocument, {}, IAlertMethods> {
47
47
  const AlertSchema = new Schema<IAlertDocument, IAlertModel, IAlertMethods>(
48
48
  {
49
49
  category: {
50
- type: String,
50
+ type: [String],
51
51
  enum: Object.values(AlertCategory),
52
52
  required: true,
53
+ validate: {
54
+ validator: function (categories: string[]) {
55
+ return categories && categories.length > 0;
56
+ },
57
+ message: "At least one category is required",
58
+ },
53
59
  },
54
60
  propertyId: {
55
61
  type: String,
@@ -198,7 +204,7 @@ AlertSchema.statics.findByCategory = function (
198
204
  category: AlertCategory,
199
205
  includeDeleted = false
200
206
  ) {
201
- const query: any = { category };
207
+ const query: any = { category: { $in: [category] } };
202
208
  if (!includeDeleted) {
203
209
  query.isDeleted = false;
204
210
  }
@@ -61,7 +61,7 @@ export class AlertRepository {
61
61
  async findAll(
62
62
  filters: {
63
63
  propertyId?: string;
64
- category?: AlertCategory;
64
+ category?: AlertCategory | AlertCategory[];
65
65
  severity?: AlertSeverity;
66
66
  entityType?: EntityType;
67
67
  entityId?: string;
@@ -77,7 +77,13 @@ export class AlertRepository {
77
77
  const query: any = {};
78
78
 
79
79
  if (filters.propertyId) query.propertyId = filters.propertyId;
80
- if (filters.category) query.category = filters.category;
80
+ if (filters.category) {
81
+ if (Array.isArray(filters.category)) {
82
+ query.category = { $in: filters.category };
83
+ } else {
84
+ query.category = { $in: [filters.category] };
85
+ }
86
+ }
81
87
  if (filters.severity) query.severity = filters.severity;
82
88
  if (filters.entityType) query.entityType = filters.entityType;
83
89
  if (filters.entityId) query.entityId = filters.entityId;
@@ -170,7 +176,7 @@ export class AlertRepository {
170
176
  async count(
171
177
  filters: {
172
178
  propertyId?: string;
173
- category?: AlertCategory;
179
+ category?: AlertCategory | AlertCategory[];
174
180
  severity?: AlertSeverity;
175
181
  entityType?: EntityType;
176
182
  entityId?: string;
@@ -183,7 +189,13 @@ export class AlertRepository {
183
189
  const query: any = {};
184
190
 
185
191
  if (filters.propertyId) query.propertyId = filters.propertyId;
186
- if (filters.category) query.category = filters.category;
192
+ if (filters.category) {
193
+ if (Array.isArray(filters.category)) {
194
+ query.category = { $in: filters.category };
195
+ } else {
196
+ query.category = { $in: [filters.category] };
197
+ }
198
+ }
187
199
  if (filters.severity) query.severity = filters.severity;
188
200
  if (filters.entityType) query.entityType = filters.entityType;
189
201
  if (filters.entityId) query.entityId = filters.entityId;
@@ -246,11 +258,20 @@ export class AlertRepository {
246
258
  * Find alerts by category
247
259
  */
248
260
  async findByCategory(
249
- category: AlertCategory,
261
+ category: AlertCategory | AlertCategory[],
250
262
  includeDeleted = false
251
263
  ): Promise<IAlertDocument[]> {
252
264
  try {
253
- return await AlertModel.findByCategory(category, includeDeleted);
265
+ if (Array.isArray(category)) {
266
+ // Use $in operator for array of categories
267
+ const query: any = { category: { $in: category } };
268
+ if (!includeDeleted) {
269
+ query.isDeleted = false;
270
+ }
271
+ return await AlertModel.find(query).sort({ createdAt: -1 });
272
+ } else {
273
+ return await AlertModel.findByCategory(category, includeDeleted);
274
+ }
254
275
  } catch (error) {
255
276
  throw new Error(
256
277
  `Failed to find alerts by category: ${
@@ -368,6 +389,7 @@ export class AlertRepository {
368
389
  ]),
369
390
  AlertModel.aggregate([
370
391
  { $match: query },
392
+ { $unwind: "$category" },
371
393
  { $group: { _id: "$category", count: { $sum: 1 } } },
372
394
  ]),
373
395
  ]);