dt-common-device 1.0.12 → 1.0.14
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.
- package/README.md +15 -35
- package/dist/config/config.d.ts +13 -0
- package/dist/config/config.js +30 -0
- package/dist/db/db.d.ts +2 -0
- package/dist/db/db.js +15 -0
- package/dist/db/index.d.ts +2 -0
- package/dist/db/index.js +18 -0
- package/dist/db/redis.d.ts +2 -0
- package/dist/db/redis.js +22 -0
- package/dist/device/cloud/entities/CloudDevice.d.ts +15 -0
- package/dist/device/cloud/entities/CloudDevice.js +42 -0
- package/dist/device/cloud/entities/DeviceFactory.d.ts +6 -0
- package/dist/device/cloud/entities/DeviceFactory.js +13 -0
- package/dist/device/cloud/entities/index.d.ts +2 -0
- package/dist/device/cloud/entities/index.js +18 -0
- package/dist/device/cloud/interfaces/ICloudDevice.d.ts +9 -0
- package/dist/device/cloud/interfaces/ICloudDevice.js +2 -0
- package/dist/device/cloud/interfaces/ICloudDeviceService.d.ts +4 -0
- package/dist/device/cloud/interfaces/ICloudDeviceService.js +2 -0
- package/dist/device/cloud/interfaces/IConnectionService.js +0 -1
- package/dist/device/cloud/interfaces/index.d.ts +2 -0
- package/dist/device/cloud/interfaces/index.js +2 -0
- package/dist/device/cloud/services/CloudDevice.service.d.ts +5 -0
- package/dist/device/cloud/services/CloudDevice.service.js +6 -0
- package/dist/device/cloud/services/Connection.service.d.ts +1 -1
- package/dist/device/cloud/services/index.d.ts +2 -3
- package/dist/device/cloud/services/index.js +16 -8
- package/dist/device/cloud/types.d.ts +9 -68
- package/dist/device/cloud/types.js +0 -12
- package/dist/device/local/interfaces/IConnection.d.ts +16 -0
- package/dist/device/local/interfaces/IConnection.js +2 -0
- package/dist/device/local/interfaces/IDevice.d.ts +4 -0
- package/dist/device/local/services/Connection.service.d.ts +5 -0
- package/dist/device/local/services/Connection.service.js +14 -0
- package/dist/device/local/services/Device.service.d.ts +7 -3
- package/dist/device/local/services/Device.service.js +85 -31
- package/dist/device/local/services/index.d.ts +2 -1
- package/dist/device/local/services/index.js +4 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +8 -27
- package/package.json +7 -9
- package/src/config/config.ts +31 -0
- package/src/db/db.ts +15 -0
- package/src/db/index.ts +2 -0
- package/src/db/redis.ts +18 -0
- package/src/device/cloud/entities/CloudDevice.ts +56 -0
- package/src/device/cloud/entities/DeviceFactory.ts +14 -0
- package/src/device/cloud/entities/index.ts +2 -0
- package/src/device/cloud/interfaces/ICloudDevice.ts +15 -0
- package/src/device/cloud/interfaces/ICloudDeviceService.ts +6 -0
- package/src/device/cloud/interfaces/IConnectionService.ts +0 -2
- package/src/device/cloud/interfaces/index.ts +2 -0
- package/src/device/cloud/services/CloudDevice.service.ts +6 -0
- package/src/device/cloud/services/Connection.service.ts +1 -1
- package/src/device/cloud/services/index.ts +2 -5
- package/src/device/cloud/types.ts +9 -63
- package/src/device/local/interfaces/IConnection.ts +17 -0
- package/src/device/local/interfaces/IDevice.ts +4 -0
- package/src/device/local/interfaces/index.ts +1 -1
- package/src/device/local/services/Connection.service.ts +15 -0
- package/src/device/local/services/Device.service.ts +107 -31
- package/src/device/local/services/index.ts +2 -1
- package/src/index.ts +9 -5
- package/src/device/cloud/services/Device.service.ts +0 -41
- package/src/device/cloud/services/Hub.service.ts +0 -34
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getPostgresClient } from "../../../db";
|
|
2
|
+
export class ConnectionService {
|
|
3
|
+
private readonly pool;
|
|
4
|
+
constructor() {
|
|
5
|
+
this.pool = getPostgresClient();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async getConnection(connectionId: string) {
|
|
9
|
+
const result = await this.pool.query(
|
|
10
|
+
"SELECT * FROM dt_connections WHERE id = $1",
|
|
11
|
+
[connectionId]
|
|
12
|
+
);
|
|
13
|
+
return result.rows[0];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//TODO: Import Redis
|
|
2
1
|
import axios from "axios";
|
|
3
2
|
import {
|
|
4
3
|
getConfig,
|
|
@@ -10,12 +9,16 @@ import { eventDispatcher } from "dt-pub-sub";
|
|
|
10
9
|
import { publishAudit } from "dt-audit-library";
|
|
11
10
|
import { EventHandler } from "../handler/EventHandler";
|
|
12
11
|
import { isEqual } from "lodash";
|
|
12
|
+
import { AlertService } from "./Alert.service";
|
|
13
|
+
import { getRedisClient, getPostgresClient } from "../../../db";
|
|
13
14
|
|
|
14
|
-
export class
|
|
15
|
+
export class LocalDeviceService {
|
|
15
16
|
private readonly baseUrl: string;
|
|
16
17
|
private readonly source = "dt-common-device";
|
|
17
18
|
private readonly eventHandler: EventHandler;
|
|
18
|
-
|
|
19
|
+
private readonly alertService: AlertService;
|
|
20
|
+
private readonly redis;
|
|
21
|
+
private readonly postgres;
|
|
19
22
|
constructor() {
|
|
20
23
|
const { DEVICE_SERVICE } = getConfig();
|
|
21
24
|
if (!DEVICE_SERVICE) {
|
|
@@ -23,10 +26,13 @@ export class DeviceService {
|
|
|
23
26
|
"DEVICE_SERVICE is not configured. Call initialize() first with DEVICE_SERVICE."
|
|
24
27
|
);
|
|
25
28
|
}
|
|
29
|
+
this.redis = getRedisClient();
|
|
30
|
+
this.postgres = getPostgresClient();
|
|
26
31
|
this.baseUrl = DEVICE_SERVICE;
|
|
27
32
|
checkAwsEnv();
|
|
28
33
|
ensureAuditInitialized();
|
|
29
34
|
this.eventHandler = new EventHandler();
|
|
35
|
+
this.alertService = new AlertService();
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
async createDevice(body: IDevice): Promise<void> {
|
|
@@ -103,14 +109,11 @@ export class DeviceService {
|
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
async setState(deviceId: string, state: any) {
|
|
106
|
-
// If old
|
|
112
|
+
// If old state and new state are different
|
|
107
113
|
const oldState = await this.getState(deviceId);
|
|
108
|
-
if (!isEqual(oldState, state)) {
|
|
109
|
-
this.eventHandler.onStateChange(deviceId, state);
|
|
114
|
+
if (!isEqual(oldState?.data?.state, state)) {
|
|
115
|
+
return await this.eventHandler.onStateChange(deviceId, state);
|
|
110
116
|
}
|
|
111
|
-
// If offline, check the time difference between the last offline status and current time
|
|
112
|
-
// Compare it with the baseline profile time of the device. If the difference is higher, then raise an operational alert
|
|
113
|
-
//deviceAlertService.raiseOperationalAlert();
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
async getStatus(deviceId: string) {
|
|
@@ -121,25 +124,43 @@ export class DeviceService {
|
|
|
121
124
|
// If old status and new status are different
|
|
122
125
|
const oldStatus = await this.getStatus(deviceId);
|
|
123
126
|
if (!isEqual(oldStatus, status)) {
|
|
124
|
-
this.eventHandler.onStatusChange(deviceId, status);
|
|
127
|
+
return await this.eventHandler.onStatusChange(deviceId, status);
|
|
128
|
+
}
|
|
129
|
+
// If both old and new status are offline, check the time difference between the last offline status and current time
|
|
130
|
+
if (
|
|
131
|
+
oldStatus?.data?.online === false &&
|
|
132
|
+
oldStatus?.data?.lastUpdated &&
|
|
133
|
+
status?.online === false
|
|
134
|
+
) {
|
|
135
|
+
const lastOfflineTime = new Date(oldStatus?.data?.lastUpdated).getTime();
|
|
136
|
+
const currentTime = Date.now();
|
|
137
|
+
const timeDifference = currentTime - lastOfflineTime;
|
|
138
|
+
// Compare it with the baseline profile time of the device. If the difference is higher, then raise an operational alert
|
|
139
|
+
if (timeDifference > 1000 * 60 * 60 * 24) {
|
|
140
|
+
return await this.alertService.raiseOperationsAlert();
|
|
141
|
+
}
|
|
125
142
|
}
|
|
126
|
-
// If offline, check the time difference between the last offline status and current time
|
|
127
|
-
// Compare it with the baseline profile time of the device. If the difference is higher, then raise an operational alert
|
|
128
|
-
//deviceAlertService.raiseOperationalAlert();
|
|
129
143
|
}
|
|
130
144
|
|
|
131
145
|
async getBatteryLevel(deviceId: string) {
|
|
132
146
|
return await axios.get(`${this.baseUrl}/devices/${deviceId}/battery-level`);
|
|
133
147
|
}
|
|
134
148
|
async setBatteryLevel(deviceId: string, batteryLevel: number) {
|
|
135
|
-
// If old
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
// If old battery level and new battery level are different
|
|
150
|
+
const oldBatteryLevel = await this.getBatteryLevel(deviceId);
|
|
151
|
+
if (!isEqual(oldBatteryLevel?.data?.value, batteryLevel)) {
|
|
152
|
+
this.eventHandler.onBatteryLevelChange(deviceId, batteryLevel);
|
|
153
|
+
}
|
|
154
|
+
// If current battery level is less than 20% and lsat updated time is more than 8 hours, raise an energy alert
|
|
155
|
+
if (
|
|
156
|
+
batteryLevel < 20 &&
|
|
157
|
+
oldBatteryLevel?.data?.value < 20 &&
|
|
158
|
+
new Date(oldBatteryLevel?.data?.lastUpdated).getTime() <
|
|
159
|
+
Date.now() - 1000 * 60 * 60 * 8
|
|
160
|
+
) {
|
|
161
|
+
//TODO: Raise an energy alert
|
|
162
|
+
await this.alertService.raiseEnergyAlert();
|
|
163
|
+
}
|
|
143
164
|
}
|
|
144
165
|
async getMetaData(deviceId: string) {
|
|
145
166
|
return await axios.get(`${this.baseUrl}/devices/${deviceId}/metaData`);
|
|
@@ -162,11 +183,25 @@ export class DeviceService {
|
|
|
162
183
|
|
|
163
184
|
async getDeviceByZone(zoneId: string) {
|
|
164
185
|
try {
|
|
165
|
-
//Step 1: Check if zone data available in
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
186
|
+
// Step 1: Check if zone data is available in Redis
|
|
187
|
+
let cached = await this.redis.get(`zone:${zoneId}`);
|
|
188
|
+
if (cached) {
|
|
189
|
+
// Parse if stored as JSON string
|
|
190
|
+
return typeof cached === "string" ? JSON.parse(cached) : cached;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// If not available, fetch from PostgreSQL DB
|
|
194
|
+
const result = await this.postgres.query(
|
|
195
|
+
"SELECT * FROM dt_devices WHERE zoneId = $1",
|
|
196
|
+
[zoneId]
|
|
197
|
+
);
|
|
198
|
+
if (result.rows.length > 0) {
|
|
199
|
+
// Store in Redis as JSON string
|
|
200
|
+
await this.redis.set(`zone:${zoneId}`, JSON.stringify(result.rows[0]));
|
|
201
|
+
return result.rows[0];
|
|
202
|
+
}
|
|
203
|
+
// If data is not available, return null
|
|
204
|
+
return null;
|
|
170
205
|
} catch (error) {
|
|
171
206
|
console.log(error);
|
|
172
207
|
throw new Error("Failed to get device by zone");
|
|
@@ -175,14 +210,55 @@ export class DeviceService {
|
|
|
175
210
|
|
|
176
211
|
async getDevicesByAccessGroup(accessGroupId: string) {
|
|
177
212
|
try {
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
213
|
+
// Try to get from Redis
|
|
214
|
+
let cached = await this.redis.get(`accessGroup:${accessGroupId}`);
|
|
215
|
+
if (cached) {
|
|
216
|
+
return typeof cached === "string" ? JSON.parse(cached) : cached;
|
|
217
|
+
}
|
|
218
|
+
// Fetch from Postgres if not in cache
|
|
219
|
+
const result = await this.postgres.query(
|
|
220
|
+
"SELECT * FROM access_groups WHERE id = $1",
|
|
221
|
+
[accessGroupId]
|
|
222
|
+
);
|
|
223
|
+
if (result.rows.length > 0) {
|
|
224
|
+
// Cache in Redis as JSON string
|
|
225
|
+
await this.redis.set(
|
|
226
|
+
`accessGroup:${accessGroupId}`,
|
|
227
|
+
JSON.stringify(result.rows[0])
|
|
228
|
+
);
|
|
229
|
+
return result.rows[0];
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
183
232
|
} catch (error) {
|
|
184
233
|
console.log(error);
|
|
185
234
|
throw new Error("Failed to get devices by access group");
|
|
186
235
|
}
|
|
187
236
|
}
|
|
237
|
+
|
|
238
|
+
async getPropertyPreferences(propertyId: string) {
|
|
239
|
+
try {
|
|
240
|
+
const propertyPreferences = await this.redis.get(
|
|
241
|
+
`propertyPreferences:${propertyId}`
|
|
242
|
+
);
|
|
243
|
+
if (!propertyPreferences) {
|
|
244
|
+
const propertyPreferences = await this.postgres.query(
|
|
245
|
+
"SELECT * FROM property_preferences WHERE property_id = $1",
|
|
246
|
+
[propertyId]
|
|
247
|
+
);
|
|
248
|
+
if (propertyPreferences.rows.length > 0) {
|
|
249
|
+
await this.redis.set(
|
|
250
|
+
`propertyPreferences:${propertyId}`,
|
|
251
|
+
propertyPreferences.rows[0]
|
|
252
|
+
);
|
|
253
|
+
return propertyPreferences.rows[0];
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
} else {
|
|
257
|
+
return propertyPreferences;
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.log(error);
|
|
261
|
+
throw new Error("Failed to get property preferences");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
188
264
|
}
|
package/src/index.ts
CHANGED
|
@@ -3,16 +3,20 @@
|
|
|
3
3
|
// Cloud exports
|
|
4
4
|
export {
|
|
5
5
|
CloudDeviceService,
|
|
6
|
-
|
|
7
|
-
CloudConnectionService,
|
|
6
|
+
ConnectionService as CloudConnectionService,
|
|
8
7
|
} from "./device/cloud/services";
|
|
9
|
-
export {
|
|
8
|
+
export { CloudDevice, DeviceFactory } from "./device/cloud/entities";
|
|
9
|
+
export {
|
|
10
|
+
LocalDeviceService,
|
|
11
|
+
LocalHubService,
|
|
12
|
+
ConnectionService,
|
|
13
|
+
} from "./device/local/services";
|
|
10
14
|
|
|
11
|
-
export *
|
|
15
|
+
export * from "./device/cloud/interfaces";
|
|
12
16
|
export * from "./device/cloud/types";
|
|
13
17
|
|
|
14
18
|
// Local exports
|
|
15
|
-
export *
|
|
19
|
+
export * from "./device/local/interfaces";
|
|
16
20
|
|
|
17
21
|
//initialize export
|
|
18
22
|
export { initialize, getConfig } from "./config/config";
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { IDeviceService } from "../interfaces";
|
|
2
|
-
import { IConnection, IDevice } from "../types";
|
|
3
|
-
|
|
4
|
-
export abstract class DeviceService implements IDeviceService {
|
|
5
|
-
// IDevice properties
|
|
6
|
-
deviceId!: string;
|
|
7
|
-
propertyId!: string;
|
|
8
|
-
name!: string;
|
|
9
|
-
hubId!: string[];
|
|
10
|
-
deviceType!: {
|
|
11
|
-
id: string;
|
|
12
|
-
type: string;
|
|
13
|
-
};
|
|
14
|
-
status!: {
|
|
15
|
-
online: boolean;
|
|
16
|
-
error?: {
|
|
17
|
-
type?: string;
|
|
18
|
-
message?: string;
|
|
19
|
-
};
|
|
20
|
-
lastUpdated?: string;
|
|
21
|
-
};
|
|
22
|
-
state?: Record<string, any>;
|
|
23
|
-
metaData?: Record<string, any>;
|
|
24
|
-
zone?: {
|
|
25
|
-
id: string;
|
|
26
|
-
name: string;
|
|
27
|
-
zoneType: string;
|
|
28
|
-
parentZone?: { id: string; name: string; zoneType: string };
|
|
29
|
-
};
|
|
30
|
-
connection!: IConnection;
|
|
31
|
-
|
|
32
|
-
constructor(device: IDevice) {
|
|
33
|
-
Object.assign(this, device);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
abstract getDevices(connection: IConnection): Promise<Record<string, any>[]>;
|
|
37
|
-
abstract getDevice(connectionId: string, deviceId: string): Promise<any>;
|
|
38
|
-
abstract getBattery(deviceId: string): Promise<number | string>;
|
|
39
|
-
abstract getStatus(connectionId: string, deviceId: string): Promise<string>;
|
|
40
|
-
abstract getState(deviceId: string): Promise<string>;
|
|
41
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { IHubService } from "../interfaces";
|
|
2
|
-
import { IConnection, IDevice } from "../types";
|
|
3
|
-
|
|
4
|
-
export abstract class HubService implements IHubService {
|
|
5
|
-
// IDevice properties
|
|
6
|
-
deviceId!: string;
|
|
7
|
-
propertyId!: string;
|
|
8
|
-
name!: string;
|
|
9
|
-
deviceType!: {
|
|
10
|
-
id: string;
|
|
11
|
-
type: string;
|
|
12
|
-
};
|
|
13
|
-
status!: {
|
|
14
|
-
online: boolean;
|
|
15
|
-
error?: {
|
|
16
|
-
type?: string;
|
|
17
|
-
message?: string;
|
|
18
|
-
};
|
|
19
|
-
lastUpdated?: string;
|
|
20
|
-
};
|
|
21
|
-
metaData?: Record<string, any>;
|
|
22
|
-
connection!: IConnection;
|
|
23
|
-
|
|
24
|
-
constructor(hub: IDevice) {
|
|
25
|
-
Object.assign(this, hub);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
abstract getHubs(connectionId: string): Promise<any[] | null>;
|
|
29
|
-
abstract getHub(
|
|
30
|
-
connectionId: string,
|
|
31
|
-
hubId: string
|
|
32
|
-
): Promise<Record<string, any>>;
|
|
33
|
-
abstract getStatus(connectionId: string, hubId: string): Promise<string>;
|
|
34
|
-
}
|