isy-nodejs 1.0.1-alpha.11 → 1.0.1-alpha.16
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/dist/cjs/ISY.js +21 -23
- package/dist/cjs/ISY.js.map +1 -1
- package/dist/cjs/Matter/Bridge/Server.js +49 -6
- package/dist/cjs/Matter/Bridge/Server.js.map +1 -1
- package/dist/cjs/Utils.js +34 -7
- package/dist/cjs/Utils.js.map +1 -1
- package/dist/cjs/types/ISY.d.ts +5 -3
- package/dist/cjs/types/ISY.d.ts.map +1 -1
- package/dist/cjs/types/Matter/Bridge/Server.d.ts +9 -0
- package/dist/cjs/types/Matter/Bridge/Server.d.ts.map +1 -1
- package/dist/cjs/types/Utils.d.ts +2 -1
- package/dist/cjs/types/Utils.d.ts.map +1 -1
- package/dist/esm/ISY.js +20 -22
- package/dist/esm/ISY.js.map +1 -1
- package/dist/esm/Matter/Bridge/Server.js +26 -6
- package/dist/esm/Matter/Bridge/Server.js.map +1 -1
- package/dist/esm/Utils.js +33 -7
- package/dist/esm/Utils.js.map +1 -1
- package/dist/esm/types/ISY.d.ts +5 -3
- package/dist/esm/types/ISY.d.ts.map +1 -1
- package/dist/esm/types/Matter/Bridge/Server.d.ts +9 -0
- package/dist/esm/types/Matter/Bridge/Server.d.ts.map +1 -1
- package/dist/esm/types/Utils.d.ts +2 -1
- package/dist/esm/types/Utils.d.ts.map +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/ISY.ts +25 -26
- package/src/Matter/Bridge/Server.ts +34 -10
- package/src/Utils.ts +39 -7
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "isy-nodejs",
|
|
3
3
|
"description": "Node.js wrapper for ISY interface including websockets for change notifications. Fork of isy-js by Rod Toll. Designed to be used in a node.js application.",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "1.0.1-alpha.
|
|
5
|
+
"version": "1.0.1-alpha.16",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Pradeep Mouli"
|
|
8
8
|
},
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "b4a88111ae80ba556773857491645f48f15f7f33"
|
|
73
73
|
}
|
package/src/ISY.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { writeFile } from 'fs';
|
|
|
3
3
|
|
|
4
4
|
import { Parser, type ParserOptions } from 'xml2js';
|
|
5
5
|
import { parseBooleans, parseNumbers } from 'xml2js/lib/processors.js';
|
|
6
|
-
import { XmlDocument } from 'xmldoc';
|
|
7
6
|
|
|
8
7
|
import axios from 'axios';
|
|
9
8
|
import { EventEmitter } from 'events';
|
|
@@ -43,8 +42,9 @@ import * as Utils from './Utils.js';
|
|
|
43
42
|
|
|
44
43
|
import { X2jOptions, XMLParser } from 'fast-xml-parser';
|
|
45
44
|
import { NodeFactory } from './Devices/NodeFactory.js';
|
|
45
|
+
import type { Config } from './Model/Config.js';
|
|
46
46
|
import { findPackageJson } from './Utils.js';
|
|
47
|
-
|
|
47
|
+
import path from 'path';
|
|
48
48
|
|
|
49
49
|
export {
|
|
50
50
|
Category as Categories,
|
|
@@ -157,7 +157,7 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
157
157
|
|
|
158
158
|
public static instance: ISY;
|
|
159
159
|
|
|
160
|
-
public configInfo:
|
|
160
|
+
public configInfo: Config;
|
|
161
161
|
public elkAlarmPanel: ELKAlarmPanelDevice;
|
|
162
162
|
public guardianTimer: any;
|
|
163
163
|
public id: string;
|
|
@@ -167,14 +167,12 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
167
167
|
public nodesLoaded: boolean = false;
|
|
168
168
|
public productId = 5226;
|
|
169
169
|
public productName = 'eisy';
|
|
170
|
-
public
|
|
170
|
+
public firmwareVersion: any;
|
|
171
171
|
public vendorName = 'Universal Devices, Inc.';
|
|
172
172
|
public webSocket: WebSocket.Client;
|
|
173
173
|
|
|
174
174
|
public apiVersion: string;
|
|
175
175
|
|
|
176
|
-
|
|
177
|
-
|
|
178
176
|
// #endregion Properties (30)
|
|
179
177
|
|
|
180
178
|
// #region Constructors (1)
|
|
@@ -230,11 +228,7 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
230
228
|
// #region Public Methods (24)
|
|
231
229
|
|
|
232
230
|
[Symbol.dispose](): void {
|
|
233
|
-
|
|
234
|
-
this.webSocket?.close();
|
|
235
|
-
} catch (e) {
|
|
236
|
-
this.logger.error(`Error closing websocket: ${e.message}`);
|
|
237
|
-
}
|
|
231
|
+
this.close();
|
|
238
232
|
}
|
|
239
233
|
|
|
240
234
|
public override emit(event: 'InitializeCompleted' | 'NodeAdded' | 'NodeRemoved' | 'NodeChanged', node?: ISYNode<any, any, any, any>): boolean {
|
|
@@ -292,7 +286,7 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
292
286
|
}
|
|
293
287
|
|
|
294
288
|
public async handleInitializeError(step: string, reason: any): Promise<any> {
|
|
295
|
-
this.logger.error(`Error initializing ISY (${step}): ${
|
|
289
|
+
this.logger.error(`Error initializing ISY (${step}): ${Utils.logStringify(reason)}`);
|
|
296
290
|
return Promise.reject(reason);
|
|
297
291
|
}
|
|
298
292
|
|
|
@@ -340,7 +334,7 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
340
334
|
}
|
|
341
335
|
break;
|
|
342
336
|
case EventType.Heartbeat:
|
|
343
|
-
this.logger.debug(`Received ${EventType[Number(stringControl)]} Signal from ISY: ${
|
|
337
|
+
this.logger.debug(`Received ${EventType[Number(stringControl)]} Signal from ISY: ${Utils.logStringify(evt)}`);
|
|
344
338
|
break;
|
|
345
339
|
|
|
346
340
|
default:
|
|
@@ -354,13 +348,13 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
354
348
|
this.logger.error(`Error handling event for ${impactedDevice.name}: ${e.message}`);
|
|
355
349
|
}
|
|
356
350
|
} else {
|
|
357
|
-
this.logger.
|
|
351
|
+
this.logger.debug(`${EventType[stringControl]} Event for Unidentified Device: ${JSON.stringify(evt)}`);
|
|
358
352
|
}
|
|
359
353
|
} else {
|
|
360
354
|
if (stringControl === EventType.NodeChanged) {
|
|
361
|
-
this.logger.
|
|
355
|
+
this.logger.debug(`Received Node Change Event: ${JSON.stringify(evt)}. These are currently unsupported.`);
|
|
362
356
|
}
|
|
363
|
-
this.logger.debug(`${EventType[Number(stringControl)]} Event: ${
|
|
357
|
+
this.logger.debug(`${EventType[Number(stringControl)]} Event: ${Utils.logStringify(evt)}`);
|
|
364
358
|
}
|
|
365
359
|
|
|
366
360
|
break;
|
|
@@ -371,7 +365,7 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
371
365
|
public async initialize(): Promise<any> {
|
|
372
366
|
const that = this;
|
|
373
367
|
try {
|
|
374
|
-
this.apiVersion = (await findPackageJson()).
|
|
368
|
+
this.apiVersion = (await findPackageJson()).version;
|
|
375
369
|
await this.loadConfig();
|
|
376
370
|
await this.loadNodes();
|
|
377
371
|
await this.loadVariables(VariableType.Integer);
|
|
@@ -435,19 +429,18 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
435
429
|
public async loadConfig(): Promise<any> {
|
|
436
430
|
try {
|
|
437
431
|
this.logger.info('Loading ISY Config');
|
|
438
|
-
const configuration = (await this.sendRequest('config')).configuration;
|
|
432
|
+
const configuration = (await this.sendRequest('config')).configuration as Config;
|
|
439
433
|
if (this.isDebugEnabled) {
|
|
440
|
-
writeFile(this.storagePath
|
|
434
|
+
writeFile(path.resolve(this.storagePath, 'ISYConfigDump.json'), JSON.stringify(configuration), this.logger.error);
|
|
441
435
|
}
|
|
442
436
|
|
|
443
437
|
const controls = configuration.controls;
|
|
444
438
|
this.model = configuration.deviceSpecs.model;
|
|
445
|
-
this.
|
|
439
|
+
this.firmwareVersion = configuration.app_full_version;
|
|
446
440
|
this.vendorName = configuration.deviceSpecs.make;
|
|
447
441
|
this.productId = configuration.product.id;
|
|
448
442
|
this.productName = configuration.product.desc;
|
|
449
443
|
this.id = configuration.root.id;
|
|
450
|
-
// TODO: Check Installed Features
|
|
451
444
|
// this.logger.info(result.configuration);
|
|
452
445
|
if (controls !== undefined) {
|
|
453
446
|
// this.logger.info(controls.control);
|
|
@@ -496,7 +489,9 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
496
489
|
}
|
|
497
490
|
}
|
|
498
491
|
|
|
499
|
-
public override on(event: '
|
|
492
|
+
public override on(event: 'initializeCompleted', listener: () => void): this;
|
|
493
|
+
public override on(event: 'nodeAdded' | 'nodeRemoved' | 'nodeChanged', listener: (node?: ISYNode<any, any, any, any>) => void): this;
|
|
494
|
+
override on(event: 'initializeCompleted' | 'nodeAdded' | 'nodeRemoved' | 'nodeChanged', listener: (node?: ISYNode<any, any, any, any>) => void): this {
|
|
500
495
|
return super.on(event, listener);
|
|
501
496
|
}
|
|
502
497
|
|
|
@@ -635,7 +630,11 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
635
630
|
}
|
|
636
631
|
|
|
637
632
|
public close() {
|
|
638
|
-
|
|
633
|
+
try {
|
|
634
|
+
this.webSocket?.close();
|
|
635
|
+
} catch (e) {
|
|
636
|
+
this.logger.error(`Error closing websocket: ${e.message}`);
|
|
637
|
+
}
|
|
639
638
|
}
|
|
640
639
|
|
|
641
640
|
// #endregion Public Methods (24)
|
|
@@ -712,17 +711,17 @@ export class ISY extends EventEmitter implements Disposable {
|
|
|
712
711
|
});
|
|
713
712
|
}
|
|
714
713
|
|
|
715
|
-
#loadElkNodes(result: any) {
|
|
714
|
+
/* #loadElkNodes(result: any) {
|
|
716
715
|
const document = new XmlDocument(result);
|
|
717
716
|
const nodes = document.childNamed('areas').childNamed('area').childrenNamed('zone');
|
|
718
717
|
for (let index = 0; index < nodes.length; index++) {
|
|
719
718
|
const id = nodes[index].attr.id;
|
|
720
719
|
const name = nodes[index].attr.name;
|
|
721
720
|
const alarmDef = nodes[index].attr.alarmDef;
|
|
722
|
-
const newDevice = new ElkAlarmSensorDevice(this, name, 1, id
|
|
721
|
+
const newDevice = new ElkAlarmSensorDevice(this, name, 1, id TODO: Handle CO Sensor vs. Door/Window Sensor );
|
|
723
722
|
this.zoneMap[newDevice.zone] = newDevice;
|
|
724
723
|
}
|
|
725
|
-
}
|
|
724
|
+
} */
|
|
726
725
|
|
|
727
726
|
async #readDeviceNodes(obj: { nodes: { node: NodeInfo[] } }) {
|
|
728
727
|
this.logger.info('Loading Device Nodes');
|
|
@@ -14,15 +14,16 @@ import { StorageService } from '@project-chip/matter.js/environment';
|
|
|
14
14
|
import { Level, levelFromString, Logger as MatterLogger } from '@project-chip/matter.js/log';
|
|
15
15
|
import { ServerNode } from '@project-chip/matter.js/node';
|
|
16
16
|
import { QrCode } from '@project-chip/matter.js/schema';
|
|
17
|
-
import { resolve } from 'path';
|
|
17
|
+
import path, { resolve } from 'path';
|
|
18
|
+
import type { ISYDeviceNode } from '../../Devices/ISYDeviceNode.js';
|
|
18
19
|
import { InsteonDimmableDevice, InsteonKeypadButtonDevice, InsteonRelayDevice, ISY } from '../../ISY.js';
|
|
19
20
|
import { ISYBridgedDeviceBehavior } from '../Behaviors/ISYBridgedDeviceBehavior.js';
|
|
20
21
|
import { ISYDimmableBehavior, ISYOnOffBehavior } from '../Behaviors/ISYOnOffBehavior.js';
|
|
21
22
|
import '../Mappings/Insteon.js';
|
|
22
|
-
import type { ISYDeviceNode } from '../../Devices/ISYDeviceNode.js';
|
|
23
23
|
|
|
24
24
|
// #region Interfaces (1)
|
|
25
25
|
|
|
26
|
+
export let instance: ServerNode;
|
|
26
27
|
export interface Config {
|
|
27
28
|
// #region Properties (8)
|
|
28
29
|
|
|
@@ -55,12 +56,21 @@ export async function createMatterServer(isy?: ISY, config?: Config): Promise<Se
|
|
|
55
56
|
try {
|
|
56
57
|
MatterLogger.addLogger(
|
|
57
58
|
'polyLogger',
|
|
58
|
-
(
|
|
59
|
+
(lvl, message) => {
|
|
60
|
+
let msg = message.slice(23).remove(Level[lvl]).trimStart();
|
|
61
|
+
let level = Level[lvl].toLowerCase().replace('notice', 'info');
|
|
62
|
+
if (msg.startsWith('EndpointStructureLogger')) {
|
|
63
|
+
if (lvl === Level.INFO) level = 'debug';
|
|
64
|
+
}
|
|
65
|
+
logger.log(level, msg);
|
|
66
|
+
},
|
|
67
|
+
/*Preserve existing formatting, but trim off date*/
|
|
59
68
|
{
|
|
60
69
|
defaultLogLevel: levelFromString(logger.level),
|
|
61
70
|
logFormat: 'plain'
|
|
62
71
|
}
|
|
63
72
|
);
|
|
73
|
+
|
|
64
74
|
} finally {
|
|
65
75
|
MatterLogger.defaultLogLevel = levelFromString(logger.level);
|
|
66
76
|
}
|
|
@@ -68,8 +78,8 @@ export async function createMatterServer(isy?: ISY, config?: Config): Promise<Se
|
|
|
68
78
|
config = await initializeConfiguration(isy, config);
|
|
69
79
|
|
|
70
80
|
logger.info(`Matter config read: ${JSON.stringify(config)}`);
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
MatterLogger.removeLogger('default');
|
|
82
|
+
let server = await ServerNode.create({
|
|
73
83
|
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
74
84
|
id: config.uniqueId,
|
|
75
85
|
|
|
@@ -216,12 +226,12 @@ export async function createMatterServer(isy?: ISY, config?: Config): Promise<Se
|
|
|
216
226
|
// logEndpoint(EndpointServer.forEndpoint(server), {logAttributePrimitiveValues: true, logAttributeObjectValues: true});
|
|
217
227
|
//else if(logger.isDebugEnabled())
|
|
218
228
|
// {
|
|
219
|
-
logEndpoint(EndpointServer.forEndpoint(server), { logAttributePrimitiveValues: true, logAttributeObjectValues: false });
|
|
229
|
+
logEndpoint(EndpointServer.forEndpoint(server), { logAttributePrimitiveValues: true, logAttributeObjectValues: false, logClusterGlobalAttributes: false });
|
|
220
230
|
// }
|
|
221
231
|
if (server.lifecycle.isOnline) {
|
|
222
232
|
const { qrPairingCode, manualPairingCode } = server.state.commissioning.pairingCodes;
|
|
223
233
|
|
|
224
|
-
logger.info(QrCode.get(qrPairingCode));
|
|
234
|
+
logger.info('/n' + QrCode.get(qrPairingCode));
|
|
225
235
|
logger.info(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
226
236
|
logger.info(`Manual pairing code: ${manualPairingCode}`);
|
|
227
237
|
}
|
|
@@ -229,22 +239,36 @@ export async function createMatterServer(isy?: ISY, config?: Config): Promise<Se
|
|
|
229
239
|
{
|
|
230
240
|
e[1].initialize(e[0] as any);
|
|
231
241
|
}*/
|
|
232
|
-
|
|
242
|
+
instance = server;
|
|
233
243
|
return server;
|
|
234
244
|
}
|
|
235
245
|
|
|
246
|
+
type PairingCodeData = {
|
|
247
|
+
qrPairingCode: string;
|
|
248
|
+
manualPairingCode: string;
|
|
249
|
+
renderedQrPairingCode: string;
|
|
250
|
+
url: string | URL;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
export function getPairingCode(server: ServerNode = instance): PairingCodeData {
|
|
254
|
+
let codes = server.state.commissioning.pairingCodes as PairingCodeData;
|
|
255
|
+
codes.renderedQrPairingCode = QrCode.get(codes.qrPairingCode);
|
|
256
|
+
codes.url = `https://project-chip.github.io/connectedhomeip/qrcode.html?data=${codes.qrPairingCode}`;
|
|
257
|
+
return codes;
|
|
258
|
+
}
|
|
259
|
+
|
|
236
260
|
async function initializeConfiguration(isy: ISY, config?: Config): Promise<Config> {
|
|
237
261
|
var logger = isy.logger;
|
|
238
262
|
|
|
239
263
|
const environment = NodeJsEnvironment();
|
|
240
264
|
|
|
241
265
|
const storageService = environment.get(StorageService);
|
|
242
|
-
environment.vars.set('storage.path',
|
|
266
|
+
environment.vars.set('storage.path', path.resolve(isy.storagePath, 'matter'));
|
|
243
267
|
environment.vars.use(() => {
|
|
244
268
|
const location = environment.vars.get('storage.path', environment.vars.get('path.root', '.'));
|
|
245
269
|
storageService.location = location;
|
|
246
270
|
});
|
|
247
|
-
|
|
271
|
+
storageService.factory = (namespace) => new StorageBackendDisk(resolve(storageService.location ?? '.', namespace), environment.vars.get('storage.clear', false));
|
|
248
272
|
logger.info(`Matter storage location: ${storageService.location} (Directory)`);
|
|
249
273
|
|
|
250
274
|
const deviceStorage = (await storageService.open('device')).createContext('data');
|
package/src/Utils.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { EventEmitter as BaseEventEmitter } from 'events';
|
|
|
8
8
|
import { Category } from './Definitions/Global/Categories.js';
|
|
9
9
|
|
|
10
10
|
//import { get } from 'http';
|
|
11
|
-
import PackageJson from '@npmcli/package-json';
|
|
11
|
+
import type { PackageJson } from '@npmcli/package-json';
|
|
12
12
|
import type { Identity } from '@project-chip/matter.js/util';
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
@@ -277,12 +277,44 @@ function getImportMeta() {
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
export async function findPackageJson(currentPath: string = getImportMeta()?.dirname): Promise<PackageJson> {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
try {
|
|
281
|
+
while (currentPath !== '/') {
|
|
282
|
+
const packageJsonPath = path.join(currentPath, 'package.json');
|
|
283
|
+
if (existsSync(packageJsonPath)) {
|
|
284
|
+
return await import(packageJsonPath);
|
|
285
|
+
}
|
|
286
|
+
currentPath = path.join(currentPath, '..');
|
|
284
287
|
}
|
|
285
|
-
|
|
286
|
-
}
|
|
288
|
+
} catch {}
|
|
287
289
|
return null;
|
|
288
290
|
}
|
|
291
|
+
|
|
292
|
+
export function logStringify(obj: any, indent = 2) {
|
|
293
|
+
let cache = [];
|
|
294
|
+
const retVal = JSON.stringify(
|
|
295
|
+
obj,
|
|
296
|
+
(key, value) => {
|
|
297
|
+
if (typeof value === 'object' && value !== null) {
|
|
298
|
+
if (cache.includes(value)) {
|
|
299
|
+
// Circular reference found, discard key
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// Store value in our collection
|
|
303
|
+
cache.push(value);
|
|
304
|
+
}
|
|
305
|
+
if (value instanceof Map) {
|
|
306
|
+
return [...value];
|
|
307
|
+
}
|
|
308
|
+
if (value instanceof Set) {
|
|
309
|
+
return [...value];
|
|
310
|
+
}
|
|
311
|
+
if (key.toLowerCase().includes('password')) {
|
|
312
|
+
return '********';
|
|
313
|
+
}
|
|
314
|
+
return value;
|
|
315
|
+
},
|
|
316
|
+
indent
|
|
317
|
+
);
|
|
318
|
+
cache = null;
|
|
319
|
+
return retVal;
|
|
320
|
+
}
|