@phystack/hub-device 4.3.40-dev

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 (82) hide show
  1. package/.prettierignore +10 -0
  2. package/.prettierrc +10 -0
  3. package/CHANGELOG.md +1202 -0
  4. package/README.md +12 -0
  5. package/dist/index.d.ts +114 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +967 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/storage/browser.d.ts +2 -0
  10. package/dist/storage/browser.d.ts.map +1 -0
  11. package/dist/storage/browser.js +20 -0
  12. package/dist/storage/browser.js.map +1 -0
  13. package/dist/storage/index.d.ts +6 -0
  14. package/dist/storage/index.d.ts.map +1 -0
  15. package/dist/storage/index.js +31 -0
  16. package/dist/storage/index.js.map +1 -0
  17. package/dist/storage/node.d.ts +2 -0
  18. package/dist/storage/node.d.ts.map +1 -0
  19. package/dist/storage/node.js +47 -0
  20. package/dist/storage/node.js.map +1 -0
  21. package/dist/sysinfo/browser.d.ts +3 -0
  22. package/dist/sysinfo/browser.d.ts.map +1 -0
  23. package/dist/sysinfo/browser.js +194 -0
  24. package/dist/sysinfo/browser.js.map +1 -0
  25. package/dist/sysinfo/index.d.ts +3 -0
  26. package/dist/sysinfo/index.d.ts.map +1 -0
  27. package/dist/sysinfo/index.js +34 -0
  28. package/dist/sysinfo/index.js.map +1 -0
  29. package/dist/sysinfo/node.d.ts +3 -0
  30. package/dist/sysinfo/node.d.ts.map +1 -0
  31. package/dist/sysinfo/node.js +53 -0
  32. package/dist/sysinfo/node.js.map +1 -0
  33. package/dist/sysinfo/tizen.d.ts +8 -0
  34. package/dist/sysinfo/tizen.d.ts.map +1 -0
  35. package/dist/sysinfo/tizen.js +168 -0
  36. package/dist/sysinfo/tizen.js.map +1 -0
  37. package/dist/types/command.types.d.ts +8 -0
  38. package/dist/types/command.types.d.ts.map +1 -0
  39. package/dist/types/command.types.js +8 -0
  40. package/dist/types/command.types.js.map +1 -0
  41. package/dist/types/container.types.d.ts +10 -0
  42. package/dist/types/container.types.d.ts.map +1 -0
  43. package/dist/types/container.types.js +3 -0
  44. package/dist/types/container.types.js.map +1 -0
  45. package/dist/types/job.types.d.ts +31 -0
  46. package/dist/types/job.types.d.ts.map +1 -0
  47. package/dist/types/job.types.js +15 -0
  48. package/dist/types/job.types.js.map +1 -0
  49. package/dist/types/twin.types.d.ts +653 -0
  50. package/dist/types/twin.types.d.ts.map +1 -0
  51. package/dist/types/twin.types.js +21 -0
  52. package/dist/types/twin.types.js.map +1 -0
  53. package/dist/utilities/get-device-identifier.utility.d.ts +2 -0
  54. package/dist/utilities/get-device-identifier.utility.d.ts.map +1 -0
  55. package/dist/utilities/get-device-identifier.utility.js +140 -0
  56. package/dist/utilities/get-device-identifier.utility.js.map +1 -0
  57. package/dist/utilities/get-hub-credentials.utility.d.ts +8 -0
  58. package/dist/utilities/get-hub-credentials.utility.d.ts.map +1 -0
  59. package/dist/utilities/get-hub-credentials.utility.js +47 -0
  60. package/dist/utilities/get-hub-credentials.utility.js.map +1 -0
  61. package/dist/utilities/get-provisioning-code.utility.d.ts +3 -0
  62. package/dist/utilities/get-provisioning-code.utility.d.ts.map +1 -0
  63. package/dist/utilities/get-provisioning-code.utility.js +49 -0
  64. package/dist/utilities/get-provisioning-code.utility.js.map +1 -0
  65. package/package.json +39 -0
  66. package/src/hub-device.d.ts +0 -0
  67. package/src/index.ts +1228 -0
  68. package/src/storage/browser.ts +16 -0
  69. package/src/storage/index.ts +46 -0
  70. package/src/storage/node.ts +42 -0
  71. package/src/sysinfo/browser.ts +217 -0
  72. package/src/sysinfo/index.ts +29 -0
  73. package/src/sysinfo/node.ts +387 -0
  74. package/src/sysinfo/tizen.ts +203 -0
  75. package/src/types/command.types.ts +8 -0
  76. package/src/types/container.types.ts +12 -0
  77. package/src/types/job.types.ts +36 -0
  78. package/src/types/twin.types.ts +751 -0
  79. package/src/utilities/get-device-identifier.utility.ts +179 -0
  80. package/src/utilities/get-hub-credentials.utility.ts +56 -0
  81. package/src/utilities/get-provisioning-code.utility.ts +55 -0
  82. package/tsconfig.json +45 -0
package/dist/index.js ADDED
@@ -0,0 +1,967 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.HubDevice = exports.getProvisioningInfo = exports.getPhyHubInstance = exports.connectToPhyHub = void 0;
40
+ const socket_io_proxy_1 = require("@phystack/socket.io-proxy");
41
+ const storage_1 = __importDefault(require("./storage"));
42
+ const sysinfo_1 = __importStar(require("./sysinfo"));
43
+ const get_device_identifier_utility_1 = __importDefault(require("./utilities/get-device-identifier.utility"));
44
+ const get_hub_credentials_utility_1 = __importDefault(require("./utilities/get-hub-credentials.utility"));
45
+ const get_provisioning_code_utility_1 = __importDefault(require("./utilities/get-provisioning-code.utility"));
46
+ const twin_types_1 = require("./types/twin.types");
47
+ const path_1 = __importDefault(require("path"));
48
+ const fs_1 = __importDefault(require("fs"));
49
+ const async_mutex_1 = require("async-mutex");
50
+ var PhyHubResponseStatus;
51
+ (function (PhyHubResponseStatus) {
52
+ PhyHubResponseStatus["success"] = "success";
53
+ PhyHubResponseStatus["error"] = "error";
54
+ })(PhyHubResponseStatus || (PhyHubResponseStatus = {}));
55
+ const logger = console;
56
+ function getVersion() {
57
+ const pkgPath = path_1.default.resolve(__dirname, '../package.json');
58
+ try {
59
+ const content = fs_1.default.readFileSync(pkgPath, 'utf-8');
60
+ const pkg = JSON.parse(content);
61
+ return pkg.version || 'N/A';
62
+ }
63
+ catch (error) {
64
+ console.error('Failed to read version from package.json:', error);
65
+ return 'N/A';
66
+ }
67
+ }
68
+ const phyhubDeviceProvisioningBaseUrls = {
69
+ LOCAL: 'https://phyhub-prov.dev.omborigrid.net/api',
70
+ DEV: 'https://phyhub-prov.dev.omborigrid.net/api',
71
+ QA: 'https://phyhub-prov.qa.omborigrid.net/api',
72
+ PROD: 'https://phyhub-prov.eu.omborigrid.net/api',
73
+ };
74
+ let phyHubSingleton = null;
75
+ const connectToPhyHub = async (gridEnv = 'PROD', osVersion = 'N/A', migratedCredentials = undefined) => {
76
+ const storage = await (0, storage_1.default)();
77
+ let deviceId = await storage.get('deviceId');
78
+ let accessKey = await storage.get('accessKey');
79
+ let phyhubUrl = await storage.get('phyhubUrl');
80
+ let deviceSerial = await storage.get('deviceSerial');
81
+ let provisioningCode = await storage.get('provisioningCode');
82
+ let region = await storage.get('region');
83
+ if (migratedCredentials) {
84
+ try {
85
+ logger.info(`connectToPhyHub(): Using migrated credentials `, JSON.stringify(migratedCredentials, null, 2));
86
+ const { deviceId: migratedDeviceId, deviceSerial: migratedDeviceSerial, accessKey: migratedAccessKey, region: migratedRegion } = migratedCredentials;
87
+ if (migratedDeviceId) {
88
+ await storage.set('deviceId', migratedDeviceId);
89
+ deviceId = migratedDeviceId;
90
+ }
91
+ if (migratedDeviceSerial) {
92
+ await storage.set('deviceSerial', migratedDeviceSerial);
93
+ deviceSerial = migratedDeviceSerial;
94
+ }
95
+ if (migratedAccessKey) {
96
+ await storage.set('accessKey', migratedAccessKey);
97
+ accessKey = migratedAccessKey;
98
+ }
99
+ if (migratedRegion) {
100
+ await storage.set('region', migratedRegion);
101
+ region = migratedRegion;
102
+ const migratedPhyhubUrl = `https://phyhub.${migratedRegion}.omborigrid.net`;
103
+ await storage.set('phyhubUrl', migratedPhyhubUrl);
104
+ phyhubUrl = migratedPhyhubUrl;
105
+ }
106
+ }
107
+ catch (error) {
108
+ logger.error(`connectToPhyHub(): Unable to migrate credentials:`, error);
109
+ }
110
+ }
111
+ if (!deviceSerial) {
112
+ logger.info('connectToPhyHub(): Getting device identifier');
113
+ deviceSerial = await (0, get_device_identifier_utility_1.default)();
114
+ await storage.set('deviceSerial', deviceSerial);
115
+ }
116
+ await storage.set('gridEnv', gridEnv);
117
+ await storage.set('osVersion', osVersion);
118
+ logger.info(`connectToPhyHub(): Connecting device`, {
119
+ deviceId,
120
+ accessKey,
121
+ deviceSerial,
122
+ gridEnv,
123
+ provisioningCode,
124
+ osVersion,
125
+ phyhubUrl,
126
+ region,
127
+ });
128
+ if (phyHubSingleton) {
129
+ return { phyHub: phyHubSingleton, deviceSerial };
130
+ }
131
+ if (deviceId && accessKey && deviceSerial && gridEnv && phyhubUrl) {
132
+ logger.info(`connectToPhyHub(): Credentials found. Connecting device ${deviceId} to phyhub.`);
133
+ phyHubSingleton = new HubDevice(phyhubUrl, deviceId, accessKey, deviceSerial, gridEnv, osVersion, region);
134
+ logger.info(`connectToPhyHub(): Device ${deviceId} connected to phyhub.`);
135
+ return { phyHub: phyHubSingleton, deviceSerial };
136
+ }
137
+ const storedDeviceSerial = await storage.get('deviceSerial');
138
+ if (!storedDeviceSerial) {
139
+ await storage.set('deviceSerial', deviceSerial);
140
+ }
141
+ const credentials = await (0, get_hub_credentials_utility_1.default)(phyhubDeviceProvisioningBaseUrls[gridEnv]);
142
+ if (credentials) {
143
+ const { deviceId, accessKey, phyhubUrl, region } = credentials;
144
+ await storage.set('region', region);
145
+ await storage.set('deviceId', deviceId);
146
+ await storage.set('accessKey', accessKey);
147
+ await storage.set('phyhubUrl', phyhubUrl);
148
+ phyHubSingleton = new HubDevice(phyhubUrl, deviceId, accessKey, deviceSerial, gridEnv, osVersion, region);
149
+ logger.info(`connectToPhyHub(): Device ${deviceId} connected to phyhub.`);
150
+ return { phyHub: phyHubSingleton, deviceSerial };
151
+ }
152
+ await (0, get_provisioning_code_utility_1.default)(phyhubDeviceProvisioningBaseUrls[gridEnv]);
153
+ const provisioningCodeKey = `provisioningCode_${gridEnv}`;
154
+ provisioningCode = await storage.get(provisioningCodeKey);
155
+ logger.info(`connectToPhyHub(): Returning provisioning code ${provisioningCode} for device ${deviceSerial}`);
156
+ return provisioningCode;
157
+ };
158
+ exports.connectToPhyHub = connectToPhyHub;
159
+ const getPhyHubInstance = async () => {
160
+ return phyHubSingleton;
161
+ };
162
+ exports.getPhyHubInstance = getPhyHubInstance;
163
+ const getProvisioningInfo = async () => {
164
+ const storage = await (0, storage_1.default)();
165
+ const deviceSerial = await storage.get('deviceSerial');
166
+ let provisioningCode = await storage.get('provisioningCode');
167
+ if (!provisioningCode) {
168
+ logger.info('getProvisioningInfo(): No provisioning code found, fetching new one');
169
+ const gridEnv = await storage.get('gridEnv') || 'PROD';
170
+ const apiUrl = phyhubDeviceProvisioningBaseUrls[gridEnv];
171
+ const result = await (0, get_provisioning_code_utility_1.default)(apiUrl);
172
+ if (result?.provisioningCode) {
173
+ provisioningCode = result.provisioningCode;
174
+ logger.info('getProvisioningInfo(): Successfully fetched new provisioning code');
175
+ }
176
+ else {
177
+ logger.error('getProvisioningInfo(): Failed to fetch provisioning code');
178
+ }
179
+ }
180
+ return {
181
+ provisioningCode,
182
+ deviceSerial,
183
+ };
184
+ };
185
+ exports.getProvisioningInfo = getProvisioningInfo;
186
+ class HubDevice {
187
+ gridEnv;
188
+ osVersion;
189
+ phyhubUrl = null;
190
+ deviceId = null;
191
+ accessKey = null;
192
+ deviceSerial = null;
193
+ region = null;
194
+ socket = null;
195
+ socketConnected = false;
196
+ socketAuthenticated = false;
197
+ onTwinUpdate = null;
198
+ onTwinRemove = null;
199
+ emitQueue = [];
200
+ emitAuthQueue = [];
201
+ scheduler = null;
202
+ deletedTwins = [];
203
+ intervals = {};
204
+ peripheralTwinMutex = new async_mutex_1.Mutex();
205
+ eventCallbacks = new Map();
206
+ reconnectListeners = [];
207
+ authReconnectListeners = [];
208
+ constructor(phyhubUrl, deviceId, accessKey, deviceSerial, gridEnv = 'PROD', osVersion = 'N/A', region) {
209
+ this.phyhubUrl = phyhubUrl;
210
+ this.deviceId = deviceId;
211
+ this.accessKey = accessKey;
212
+ this.deviceSerial = deviceSerial;
213
+ this.gridEnv = gridEnv.toUpperCase();
214
+ this.osVersion = osVersion;
215
+ this.region = region.toUpperCase();
216
+ }
217
+ setTwinUpdateHandler(callback) {
218
+ this.onTwinUpdate = callback;
219
+ }
220
+ setTwinRemoveHandler(callback) {
221
+ this.onTwinRemove = callback;
222
+ }
223
+ isConnected() {
224
+ return this.socketConnected;
225
+ }
226
+ isAuthenticated() {
227
+ return this.socketAuthenticated;
228
+ }
229
+ async connect() {
230
+ try {
231
+ const { deviceId, accessKey, gridEnv, osVersion, phyhubUrl } = this;
232
+ if (!deviceId || !accessKey) {
233
+ logger.error('connect(): DeviceId or AccessKey not set');
234
+ return;
235
+ }
236
+ if (!phyhubUrl) {
237
+ logger.error('connect(): Phyhub URL not set');
238
+ return;
239
+ }
240
+ logger.info(`connect(): Connecting device `, { deviceId, accessKey, gridEnv, osVersion });
241
+ this.socket = await this.connectSocket(deviceId, accessKey, phyhubUrl);
242
+ }
243
+ catch (error) {
244
+ logger.error(`connect(): Failed to connect socket`, error);
245
+ }
246
+ return this;
247
+ }
248
+ on(event, callback) {
249
+ const { socket, deviceId } = this;
250
+ if (!this.eventCallbacks.has(event)) {
251
+ this.eventCallbacks.set(event, []);
252
+ }
253
+ this.eventCallbacks.get(event).push(callback);
254
+ if (!socket || !deviceId) {
255
+ logger.error('SOCKET on(): No socket or deviceId available', { socket: !!socket, deviceId });
256
+ return callback({ error: `connectDevice ${deviceId}: Not connected` });
257
+ }
258
+ socket.on(event, (data) => {
259
+ if (this.isConnected()) {
260
+ this.eventCallbacks.get(event).forEach(cb => cb(data));
261
+ }
262
+ else {
263
+ logger.warn(`SOCKET on(): Ignoring ${event} event as socket is not connected`, { deviceId });
264
+ }
265
+ });
266
+ logger.info('SOCKET on(): Successfully registered callback for event', { event, deviceId });
267
+ }
268
+ emit(...args) {
269
+ const { socket, emitQueue } = this;
270
+ if (!socket) {
271
+ logger.error('emit(): Socket not created');
272
+ return;
273
+ }
274
+ if (!this.isConnected()) {
275
+ logger.info('SOCKET emit(): Pushing to emitQueue');
276
+ emitQueue.push(args);
277
+ return;
278
+ }
279
+ socket.emit.apply(socket, args);
280
+ }
281
+ emitAuth(...args) {
282
+ const { socket, emitAuthQueue } = this;
283
+ if (!socket) {
284
+ logger.error('emitAuth(): Socket not created');
285
+ return;
286
+ }
287
+ if (!this.isConnected() || !this.isAuthenticated()) {
288
+ emitAuthQueue.push(args);
289
+ return;
290
+ }
291
+ socket.emit.apply(socket, args);
292
+ }
293
+ processEmitQueue() {
294
+ logger.info('SOCKET processEmitQueue(): processing emit queue');
295
+ const { socket, emitQueue } = this;
296
+ if (!socket || !this.isConnected()) {
297
+ logger.info('SOCKET processEmitQueue(): Aborting', {
298
+ socket: !!socket,
299
+ socketConnected: this.isConnected(),
300
+ });
301
+ return;
302
+ }
303
+ while (emitQueue.length > 0) {
304
+ const args = emitQueue.shift();
305
+ socket.emit.apply(socket, args);
306
+ }
307
+ this.processEmitAuthQueue();
308
+ }
309
+ processEmitAuthQueue() {
310
+ logger.info('SOCKET processEmitAuthQueue(): processing emit auth queue');
311
+ const { socket, emitAuthQueue } = this;
312
+ if (!socket || !this.isConnected() || !this.isAuthenticated()) {
313
+ logger.info('SOCKET processEmitAuthQueue(): Aborting', {
314
+ socket: !!socket,
315
+ socketConnected: this.isConnected(),
316
+ socketAuthenticated: this.isAuthenticated(),
317
+ });
318
+ return;
319
+ }
320
+ while (emitAuthQueue.length > 0) {
321
+ const args = emitAuthQueue.shift();
322
+ socket.emit.apply(socket, args);
323
+ }
324
+ }
325
+ executeReconnectListeners() {
326
+ this.reconnectListeners.forEach(listener => {
327
+ try {
328
+ listener();
329
+ }
330
+ catch (error) {
331
+ logger.error('Error executing reconnect listener:', error);
332
+ }
333
+ });
334
+ }
335
+ executeAuthReconnectListeners() {
336
+ this.authReconnectListeners.forEach(listener => {
337
+ try {
338
+ listener();
339
+ }
340
+ catch (error) {
341
+ logger.error('Error executing reconnect listener:', error);
342
+ }
343
+ });
344
+ }
345
+ async connectSocket(deviceId, accessKey, phyhubUrl) {
346
+ const socket = await (0, socket_io_proxy_1.getSocketIOWithProxy)(phyhubUrl, {
347
+ auth: { deviceId, accessKey },
348
+ reconnection: true,
349
+ reconnectionDelayMax: 1000,
350
+ reconnectionAttempts: Infinity,
351
+ });
352
+ socket.on('connect', () => {
353
+ logger.info(`SOCKET connectSocket() on connect: Connected to phyhub ${phyhubUrl} 🟢-🟢-🟢-🟢-⚪`);
354
+ this.socketConnected = true;
355
+ this.processEmitQueue();
356
+ if (this.socketConnected) {
357
+ this.executeReconnectListeners();
358
+ }
359
+ if (this.socketAuthenticated) {
360
+ logger.info('SOCKET connectSocket() on reconnect: Socket already authenticated, notifying listeners');
361
+ this.executeAuthReconnectListeners();
362
+ this.processEmitAuthQueue();
363
+ }
364
+ });
365
+ socket.on('disconnect', () => {
366
+ logger.info('SOCKET connectSocket() on disconnect: Disconnected from server');
367
+ this.socketConnected = false;
368
+ });
369
+ socket.on('deviceAuthenticated', (response) => {
370
+ logger.info('SOCKET connectSocket() on deviceAuthenticated: Device authenticated', response);
371
+ this.socketAuthenticated = true;
372
+ if (response.status === 'success') {
373
+ this.executeReconnectListeners();
374
+ this.processEmitAuthQueue();
375
+ }
376
+ else {
377
+ logger.error('SOCKET connectSocket() on deviceAuthenticated: Device authentication failed', response);
378
+ }
379
+ });
380
+ socket.io.on('reconnect', (attempt) => {
381
+ logger.info(`SOCKET connectSocket() on reconnect: Reconnected to server attempt: ${attempt}`);
382
+ this.socketConnected = true;
383
+ this.processEmitQueue();
384
+ });
385
+ socket.on('twinUpdated', async (payload) => {
386
+ logger.info('SOCKET connectSocket() on twinUpdated: syncing twin on device');
387
+ if (!payload.data)
388
+ return;
389
+ const twin = payload.data;
390
+ await this.cacheTwins([twin]);
391
+ if (this.onTwinUpdate) {
392
+ this.onTwinUpdate(twin);
393
+ }
394
+ });
395
+ socket.on('twinCreated', async (payload) => {
396
+ logger.info('SOCKET connectSocket() on twinCreated: Adding new twin to device');
397
+ if (payload.data) {
398
+ await this.cacheTwins([payload.data]);
399
+ if (this.onTwinUpdate) {
400
+ this.onTwinUpdate(payload.data);
401
+ }
402
+ }
403
+ });
404
+ socket.on('twinDeleted', async (payload) => {
405
+ logger.info('SOCKET connectSocket() on twinDeleted: Removing twin from device');
406
+ if (this.onTwinRemove && payload.data) {
407
+ await this.removeCachedTwin([payload.data]);
408
+ this.onTwinRemove(payload.data);
409
+ }
410
+ });
411
+ socket.io.on('reconnect_attempt', (attempt) => {
412
+ logger.info(`SOCKET connectSocket() on reconnect_attempt: Reconnect attempt: ${attempt}`);
413
+ });
414
+ socket.io.on('reconnect_error', (error) => {
415
+ logger.info('SOCKET connectSocket() on reconnect_error: ', error);
416
+ });
417
+ socket.io.on('reconnect_failed', () => {
418
+ logger.info('SOCKET connectSocket() on reconnect_failed: Reconnect failed');
419
+ });
420
+ return socket;
421
+ }
422
+ getDeviceStatus = async () => {
423
+ const storage = await (0, storage_1.default)();
424
+ const deviceId = await storage.get('deviceId');
425
+ const accessKey = await storage.get('accessKey');
426
+ const deviceSerial = await storage.get('deviceSerial');
427
+ const provisioningCode = accessKey ? 'N/A' : await storage.get('provisioningCode');
428
+ const gridEnv = await storage.get('gridEnv');
429
+ const osVersion = await storage.get('osVersion');
430
+ const dataResidency = await storage.get('region');
431
+ const twins = await this.getCachedTwins();
432
+ let displayName = '';
433
+ let deviceEnv = '';
434
+ let spaceId = '';
435
+ let tenantId = '';
436
+ let ip = [];
437
+ const cachedTwins = await this.getCachedTwins();
438
+ try {
439
+ const deviceTwinInstanceId = cachedTwins?.[twin_types_1.TwinTypeEnum.Device]?.[0];
440
+ if (deviceTwinInstanceId) {
441
+ const deviceTwinInstance = await this.getCachedTwins(deviceTwinInstanceId);
442
+ if (deviceTwinInstance) {
443
+ displayName = deviceTwinInstance.properties.desired.displayName;
444
+ deviceEnv = deviceTwinInstance.properties.desired.env;
445
+ spaceId = deviceTwinInstance.properties.desired.spaceId;
446
+ tenantId = deviceTwinInstance.tenantId;
447
+ ip = deviceTwinInstance.properties.reported.ip;
448
+ }
449
+ }
450
+ }
451
+ catch (error) {
452
+ logger.error(`Device twin not found for device ${deviceId}`, error);
453
+ }
454
+ const response = {
455
+ deviceId,
456
+ accessKey,
457
+ deviceSerial,
458
+ gridEnv,
459
+ osVersion,
460
+ provisioningCode,
461
+ displayName,
462
+ deviceEnv,
463
+ spaceId,
464
+ tenantId,
465
+ dataResidency,
466
+ ip,
467
+ socketConnected: this.isConnected(),
468
+ socketAuthenticated: this.isAuthenticated(),
469
+ twins,
470
+ };
471
+ return response;
472
+ };
473
+ getDeviceInstance = async () => {
474
+ const storage = await (0, storage_1.default)();
475
+ const deviceId = await storage.get('deviceId');
476
+ const twins = await this.getCachedTwins();
477
+ try {
478
+ const deviceTwinInstanceId = twins[twin_types_1.TwinTypeEnum.Device][0];
479
+ if (deviceTwinInstanceId) {
480
+ const deviceTwinInstance = await this.getCachedTwins(deviceTwinInstanceId);
481
+ if (deviceTwinInstance) {
482
+ return { twin: deviceTwinInstance };
483
+ }
484
+ }
485
+ return {};
486
+ }
487
+ catch (error) {
488
+ logger.error(`Device twin not found for device ${deviceId}`, error);
489
+ return {};
490
+ }
491
+ };
492
+ getDeviceNetworks = async () => {
493
+ try {
494
+ const sysinfo = await (0, sysinfo_1.default)();
495
+ const { networkInterfaces, wifiInterfaces, wifiNetworks, wifiConnections } = sysinfo;
496
+ return {
497
+ networkInterfaces,
498
+ wifiInterfaces,
499
+ wifiNetworks,
500
+ wifiConnections,
501
+ };
502
+ }
503
+ catch (error) {
504
+ logger.error(`Unable to get network information of the device`, error);
505
+ return {
506
+ wifiNetworks: [],
507
+ wifiConnections: [],
508
+ networkInterfaces: [],
509
+ wifiInterfaces: [],
510
+ };
511
+ }
512
+ };
513
+ getScreenInstance = async () => {
514
+ const storage = await (0, storage_1.default)();
515
+ const deviceId = await storage.get('deviceId');
516
+ try {
517
+ const twins = await this.getCachedTwins();
518
+ console.log('getScreenInstance(): twins', JSON.stringify(twins, null, 2));
519
+ if (!twins[twin_types_1.TwinTypeEnum.Screen]) {
520
+ return {};
521
+ }
522
+ const screenTwinInstanceId = twins[twin_types_1.TwinTypeEnum.Screen][0];
523
+ if (screenTwinInstanceId) {
524
+ const screenTwinInstance = await this.getCachedTwins(screenTwinInstanceId);
525
+ if (screenTwinInstance) {
526
+ return { twin: screenTwinInstance };
527
+ }
528
+ }
529
+ return {};
530
+ }
531
+ catch (error) {
532
+ logger.error(`Screen instance not found for device ${deviceId}`, error);
533
+ return {};
534
+ }
535
+ };
536
+ async connectDevice(callback) {
537
+ const { socket, deviceId } = this;
538
+ logger.info(`connectDevice(): Connecting device ${deviceId}`);
539
+ if (!socket || !deviceId) {
540
+ return callback({ error: `connectDevice ${deviceId}: Not connected` });
541
+ }
542
+ let deviceTwin = undefined;
543
+ let cachedTwinsInfo;
544
+ let shouldUseCachedResponse = false;
545
+ try {
546
+ cachedTwinsInfo = await this.getCachedTwins();
547
+ if (cachedTwinsInfo && cachedTwinsInfo[twin_types_1.TwinTypeEnum.Device] && cachedTwinsInfo[twin_types_1.TwinTypeEnum.Device].length > 0) {
548
+ const deviceTwinId = cachedTwinsInfo[twin_types_1.TwinTypeEnum.Device][0];
549
+ deviceTwin = await this.getCachedTwins(deviceTwinId);
550
+ shouldUseCachedResponse = deviceTwin && deviceTwin.properties && deviceTwin.properties.desired;
551
+ }
552
+ }
553
+ catch (error) {
554
+ logger.info(`connectDevice(): No cached twins found, will fetch fresh twins`);
555
+ }
556
+ const reportSystemInformation = async () => {
557
+ logger.info('reportSystemInformation(): About to fetch system information');
558
+ const deviceInfo = await (0, sysinfo_1.default)();
559
+ let ips = [];
560
+ try {
561
+ const { networkInterfaces } = deviceInfo;
562
+ for (const iface of networkInterfaces) {
563
+ if (iface.ip4 && iface.ip4 !== '127.0.0.1') {
564
+ ips.push({
565
+ interface: iface.ifaceName,
566
+ ipv4: iface.ip4,
567
+ ipv6: iface.ip6,
568
+ });
569
+ }
570
+ }
571
+ }
572
+ catch (error) {
573
+ logger.error('Failed to detect IP address of device', error);
574
+ }
575
+ const storage = await (0, storage_1.default)();
576
+ const isBrowser = typeof window !== 'undefined';
577
+ let finalOsVersion;
578
+ if (sysinfo_1.isTizen) {
579
+ finalOsVersion = deviceInfo?.os?.osVersion || '';
580
+ }
581
+ else if (isBrowser && window.location && window.location.pathname) {
582
+ const segments = window.location.pathname.split('/').filter(Boolean);
583
+ finalOsVersion = segments.length ? segments[segments.length - 1] : 'N/A';
584
+ }
585
+ else {
586
+ finalOsVersion = (await storage.get('osVersion')) || 'N/A';
587
+ }
588
+ const gridEnv = await storage.get('gridEnv');
589
+ const deviceTwinReportedProperties = {
590
+ data: {
591
+ ...deviceInfo,
592
+ ip: deviceInfo?.ip && deviceInfo?.ip?.length > 0 ? deviceInfo.ip : ips,
593
+ os: {
594
+ osVersion: finalOsVersion,
595
+ },
596
+ env: {
597
+ gridEnv,
598
+ },
599
+ phydeviceVersion: getVersion(),
600
+ },
601
+ };
602
+ logger.info('reportSystemInformation(): About to emit system information');
603
+ socket.emit('reportDeviceTwinProperties', deviceTwinReportedProperties, async (_response) => {
604
+ logger.info('reportSystemInformation(): Emitted system information');
605
+ });
606
+ };
607
+ reportSystemInformation();
608
+ if (!this.intervals['reportSystemInformation']) {
609
+ this.intervals['reportSystemInformation'] = setInterval(async () => {
610
+ if (!this.isConnected() || !this.isAuthenticated()) {
611
+ return;
612
+ }
613
+ reportSystemInformation();
614
+ }, 300000);
615
+ }
616
+ const connector = async () => {
617
+ this.emitAuth('connectDevice', async (response) => {
618
+ console.log('connector connectDevice(): response', response);
619
+ if (response.status === 'success' && response.twins) {
620
+ callback(response);
621
+ const { twins } = response;
622
+ logger.info(`SOCKET connector(): received ${twins.length} twins in response. 🟢-🟢-🟢-🟢-🟢`);
623
+ await this.cacheTwins(twins);
624
+ }
625
+ else if (shouldUseCachedResponse && deviceTwin && cachedTwinsInfo) {
626
+ const storage = await (0, storage_1.default)();
627
+ const region = await storage.get('region');
628
+ const phyhubUrl = await storage.get('phyhubUrl');
629
+ const transformedTwins = await this.transformCachedTwinsToArray(cachedTwinsInfo);
630
+ logger.info('connectDevice(): Using cached twins as fallback due to server error');
631
+ callback({ status: 'success', twins: transformedTwins, region, phyhubUrl });
632
+ }
633
+ else {
634
+ logger.error(`SOCKET connector(): No twins received in response and no cached fallback available. 🟢-🟢-🟢-🟢-🔴`);
635
+ callback(response);
636
+ }
637
+ });
638
+ };
639
+ connector();
640
+ socket.on('reconnect', connector);
641
+ }
642
+ async transformCachedTwinsToArray(cachedTwins) {
643
+ const transformedTwins = [];
644
+ for (const [_type, twinIds] of Object.entries(cachedTwins)) {
645
+ for (const twinId of twinIds) {
646
+ const twin = await this.getCachedTwins(twinId);
647
+ if (twin) {
648
+ transformedTwins.push(twin);
649
+ }
650
+ }
651
+ }
652
+ return transformedTwins;
653
+ }
654
+ cacheTwins = async (twins) => {
655
+ for (const twin of twins) {
656
+ try {
657
+ if (twin.type === twin_types_1.TwinTypeEnum.Peripheral) {
658
+ await this.cachePeripheralTwins(twin);
659
+ continue;
660
+ }
661
+ const { type, id } = twin;
662
+ if (type === twin_types_1.TwinTypeEnum.Device) {
663
+ const reported = twin.properties?.reported;
664
+ const jobs = reported?.jobs ?? [];
665
+ if (jobs.length > 0 && this.scheduler) {
666
+ this.scheduler.processJobs(jobs, twin?.properties?.desired?.timezone ?? 'UTC', id);
667
+ }
668
+ }
669
+ }
670
+ catch (error) {
671
+ logger.error(`cacheTwins(): Failed to schedule job for device twin ${twin.id}`, error);
672
+ }
673
+ }
674
+ const storage = await (0, storage_1.default)();
675
+ const twinsInfo = {};
676
+ let twinsInfoCachePath = ``;
677
+ for (const twin of twins) {
678
+ if (twin && !this.deletedTwins.includes(twin.id)) {
679
+ const { type, deviceId, id } = twin;
680
+ twinsInfoCachePath = `twins-device-${deviceId}`;
681
+ const twinCachePath = `${id}-device-${deviceId}`;
682
+ await storage.set(twinCachePath, twin);
683
+ if (!twinsInfo[type]) {
684
+ twinsInfo[type] = [];
685
+ }
686
+ twinsInfo[type].push(id);
687
+ }
688
+ }
689
+ let cachedTwinsInfo = undefined;
690
+ try {
691
+ cachedTwinsInfo = await storage.get(twinsInfoCachePath);
692
+ }
693
+ catch (error) {
694
+ }
695
+ if (cachedTwinsInfo) {
696
+ const updatedTwinsInfo = { ...cachedTwinsInfo };
697
+ for (const key in twinsInfo) {
698
+ if (Object.prototype.hasOwnProperty.call(updatedTwinsInfo, key)) {
699
+ updatedTwinsInfo[key] = Array.from(new Set([...updatedTwinsInfo[key], ...twinsInfo[key]]));
700
+ }
701
+ else {
702
+ updatedTwinsInfo[key] = twinsInfo[key];
703
+ }
704
+ }
705
+ await storage.set(twinsInfoCachePath, updatedTwinsInfo);
706
+ }
707
+ else {
708
+ await storage.set(twinsInfoCachePath, twinsInfo);
709
+ }
710
+ };
711
+ async removeCachedTwin(twins) {
712
+ const storage = await (0, storage_1.default)();
713
+ const twinsArray = Array.isArray(twins) ? twins : [twins];
714
+ for (const twin of twinsArray) {
715
+ const twinData = typeof twin === 'string'
716
+ ? await this.getCachedTwins(twin)
717
+ : twin;
718
+ if (!twinData)
719
+ continue;
720
+ const { type, deviceId, id, instanceId } = twinData;
721
+ if (type === twin_types_1.TwinTypeEnum.Peripheral && instanceId) {
722
+ await this.peripheralTwinMutex.runExclusive(async () => {
723
+ const cachePath = `peripheral-twins-${instanceId}`;
724
+ const peripheralTwins = await storage.get(cachePath) || {};
725
+ delete peripheralTwins[id];
726
+ await storage.set(cachePath, peripheralTwins);
727
+ });
728
+ continue;
729
+ }
730
+ const twinCachePath = `${id}-device-${deviceId}`;
731
+ const twinsInfoCachePath = `twins-device-${deviceId}`;
732
+ try {
733
+ logger.info(`removeCachedTwin(): Removing ${type} twin ${id}`);
734
+ await storage.remove(twinCachePath);
735
+ const cachedTwinsInfo = await storage.get(twinsInfoCachePath);
736
+ if (cachedTwinsInfo && cachedTwinsInfo[type]) {
737
+ cachedTwinsInfo[type] = cachedTwinsInfo[type].filter((cachedId) => cachedId !== id);
738
+ if (cachedTwinsInfo[type].length === 0) {
739
+ delete cachedTwinsInfo[type];
740
+ }
741
+ if (Object.keys(cachedTwinsInfo).length > 0) {
742
+ await storage.set(twinsInfoCachePath, cachedTwinsInfo);
743
+ }
744
+ else {
745
+ await storage.remove(twinsInfoCachePath);
746
+ }
747
+ }
748
+ this.deletedTwins.push(id);
749
+ }
750
+ catch (error) {
751
+ logger.info(`removeCachedTwin(): No twins info cache found for ${twinsInfoCachePath}`);
752
+ }
753
+ }
754
+ }
755
+ getCachedTwins = async (twinId = undefined) => {
756
+ const storage = await (0, storage_1.default)();
757
+ if (twinId) {
758
+ const twinCachePath = `${twinId}-device-${this.deviceId}`;
759
+ const cachedTwin = await storage.get(twinCachePath);
760
+ return cachedTwin;
761
+ }
762
+ else {
763
+ const twinsInfoCachePath = `twins-device-${this.deviceId}`;
764
+ const cachedTwinsInfo = await storage.get(twinsInfoCachePath) || {};
765
+ return cachedTwinsInfo;
766
+ }
767
+ };
768
+ async reportScreenInstanceProperties(properties, callback) {
769
+ const { socket, deviceId } = this;
770
+ if (!socket || !deviceId) {
771
+ return callback({ error: `reportScreenInstanceProperties() ${deviceId}: Not connected` });
772
+ }
773
+ this.emitAuth('reportScreenTwinProperties', properties, async (response) => {
774
+ if (response.status === PhyHubResponseStatus.success) {
775
+ const { twin } = response;
776
+ if (!twin) {
777
+ logger.info(`reportScreenInstanceProperties() on reportScreenProperties: failed to cache screen twin updates for device ${deviceId}`);
778
+ }
779
+ else {
780
+ await this.cacheTwins([twin]);
781
+ }
782
+ }
783
+ else {
784
+ logger.info(`reportScreenInstanceProperties() on reportScreenProperties: failed to report screen app properties of device ${deviceId}`);
785
+ }
786
+ callback(response);
787
+ });
788
+ }
789
+ async getEdgeInstance(payload) {
790
+ const storage = await (0, storage_1.default)();
791
+ const deviceId = await storage.get('deviceId');
792
+ try {
793
+ const { twinId } = payload;
794
+ const twins = await this.getCachedTwins();
795
+ const edgeTwin = twins[twin_types_1.TwinTypeEnum.Edge].filter((id) => id === twinId);
796
+ if (edgeTwin && edgeTwin.length > 0) {
797
+ const edgeTwinInstance = await this.getCachedTwins(edgeTwin[0]);
798
+ if (edgeTwinInstance) {
799
+ return { twin: edgeTwinInstance };
800
+ }
801
+ }
802
+ return {};
803
+ }
804
+ catch (error) {
805
+ logger.error(`Edge instance not found for device ${deviceId}`, error);
806
+ return {};
807
+ }
808
+ }
809
+ async sendEventSignal(payload) {
810
+ logger.info(`sendEventSignal(): sending Event Signal from device ${this.deviceId}`, payload);
811
+ const { socket, deviceId } = this;
812
+ if (!socket || !deviceId) {
813
+ logger.error(`sendEventSignal(): Device or socket not connected`);
814
+ return;
815
+ }
816
+ this.emitAuth('signalEvent', payload);
817
+ }
818
+ async sendClientSignal(payload) {
819
+ logger.info(`sendClientSignal(): sending Client Signal from device ${this.deviceId}`, payload);
820
+ const { socket, deviceId } = this;
821
+ if (!socket || !deviceId) {
822
+ logger.error(`sendClientSignal(): Device or socket not connected`);
823
+ return;
824
+ }
825
+ this.emitAuth('signalClient', payload);
826
+ }
827
+ async sendSessionSignal(payload) {
828
+ logger.info(`sendSessionSignal(): sending Session Signal from device ${this.deviceId}`, payload);
829
+ const { socket, deviceId } = this;
830
+ if (!socket || !deviceId) {
831
+ logger.error(`sendSessionSignal(): Device or socket not connected`);
832
+ return;
833
+ }
834
+ this.emitAuth('signalSession', payload);
835
+ }
836
+ async reportEdgeTwinProperties(instanceTwinId, properties, callback) {
837
+ logger.info(`reportEdgeTwinProperties(): Reporting twin ${instanceTwinId} properties`);
838
+ const { socket, deviceId } = this;
839
+ if (!socket || !deviceId) {
840
+ return callback({ error: `reportEdgeTwinProperties(): Device or socket not connected` });
841
+ }
842
+ const payload = {
843
+ twinId: instanceTwinId,
844
+ data: properties,
845
+ };
846
+ this.emitAuth('reportEdgeTwinProperties', payload, async (response) => {
847
+ const { twin } = response;
848
+ if (twin) {
849
+ await this.cacheTwins([twin]);
850
+ }
851
+ });
852
+ }
853
+ async reportDeviceTwinProperties(instanceTwinId, properties, callback) {
854
+ const { socket, deviceId } = this;
855
+ if (!socket || !deviceId) {
856
+ return callback({ error: `reportDeviceTwinProperties(): Device or socket not connected` });
857
+ }
858
+ const payload = {
859
+ twinId: instanceTwinId,
860
+ data: properties,
861
+ };
862
+ this.emitAuth('reportDeviceTwinProperties', payload, async (response) => {
863
+ const { twin } = response;
864
+ if (twin) {
865
+ await this.cacheTwins([twin]);
866
+ }
867
+ });
868
+ }
869
+ async reportDeviceJob(instanceTwinId, job, callback) {
870
+ const { socket, deviceId } = this;
871
+ if (!socket || !deviceId) {
872
+ return callback({ error: `reportDeviceJob(): Device or socket not connected` });
873
+ }
874
+ const payload = {
875
+ twinId: instanceTwinId,
876
+ data: job,
877
+ };
878
+ this.emitAuth('reportDeviceJob', payload, async (response) => {
879
+ const { twin } = response;
880
+ if (twin) {
881
+ await this.cacheTwins([twin]);
882
+ }
883
+ });
884
+ }
885
+ async reportPeripheralTwinProperties(instanceTwinId, properties, callback) {
886
+ const { socket, deviceId } = this;
887
+ if (!socket || !deviceId) {
888
+ return callback?.({ error: `reportPeripheralTwinProperties(): Device or socket not connected` });
889
+ }
890
+ const payload = {
891
+ twinId: instanceTwinId,
892
+ data: properties,
893
+ };
894
+ this.emitAuth('reportPeripheralTwinProperties', payload, async (response) => {
895
+ const { twin } = response;
896
+ if (twin) {
897
+ await this.cacheTwins([twin]);
898
+ }
899
+ callback?.(response);
900
+ });
901
+ }
902
+ setScheduler(scheduler) {
903
+ this.scheduler = scheduler;
904
+ if (scheduler && typeof scheduler.setHubDevice === 'function') {
905
+ scheduler.setHubDevice(this);
906
+ }
907
+ logger.info('setScheduler(): Scheduler has been set');
908
+ }
909
+ async cachePeripheralTwins(twin) {
910
+ if (twin.type !== twin_types_1.TwinTypeEnum.Peripheral || !twin.instanceId)
911
+ return;
912
+ return await this.peripheralTwinMutex.runExclusive(async () => {
913
+ const storage = await (0, storage_1.default)();
914
+ const cachePath = `peripheral-twins-${twin.instanceId}`;
915
+ let peripheralTwins = await storage.get(cachePath) || {};
916
+ peripheralTwins[twin.id] = twin;
917
+ await storage.set(cachePath, peripheralTwins);
918
+ return peripheralTwins;
919
+ });
920
+ }
921
+ async getPeripheralTwins(payload) {
922
+ const { data } = payload;
923
+ if (!data?.instanceId) {
924
+ logger.error('getPeripheralTwins(): Missing instanceId in payload');
925
+ return { peripheralTwins: {} };
926
+ }
927
+ return await this.peripheralTwinMutex.runExclusive(async () => {
928
+ const storage = await (0, storage_1.default)();
929
+ const cachePath = `peripheral-twins-${data.instanceId}`;
930
+ const cachedTwins = await storage.get(cachePath) || {};
931
+ try {
932
+ const response = await new Promise((resolve, reject) => {
933
+ this.emitAuth('getPeripheralTwins', payload, resolve);
934
+ this.socket?.on('error', reject);
935
+ });
936
+ const peripheralTwins = response?.peripheralTwins || {};
937
+ for (const twin of Object.values(peripheralTwins)) {
938
+ await this.cachePeripheralTwins(twin);
939
+ }
940
+ const cachedTwinIds = Object.keys(cachedTwins);
941
+ const currentTwinIds = Object.keys(peripheralTwins);
942
+ const deletedTwinIds = cachedTwinIds.filter(id => !currentTwinIds.includes(id));
943
+ if (deletedTwinIds.length > 0) {
944
+ await this.removeCachedTwin(deletedTwinIds.map(id => cachedTwins[id]));
945
+ }
946
+ return { peripheralTwins };
947
+ }
948
+ catch (error) {
949
+ logger.error('getPeripheralTwins(): Error during execution', error);
950
+ return { peripheralTwins: {} };
951
+ }
952
+ });
953
+ }
954
+ onReconnect(listener) {
955
+ this.reconnectListeners.push(listener);
956
+ }
957
+ onAuthReconnect(listener) {
958
+ this.authReconnectListeners.push(listener);
959
+ }
960
+ }
961
+ exports.HubDevice = HubDevice;
962
+ exports.default = {
963
+ connectToPhyHub: exports.connectToPhyHub,
964
+ getPhyHubInstance: exports.getPhyHubInstance,
965
+ getProvisioningInfo: exports.getProvisioningInfo,
966
+ };
967
+ //# sourceMappingURL=index.js.map