homebridge-zwave-usb 3.6.2 → 3.6.3
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.
|
@@ -32,6 +32,11 @@ export declare class ZWaveAccessory {
|
|
|
32
32
|
}): void;
|
|
33
33
|
private getDesiredName;
|
|
34
34
|
private getEffectiveName;
|
|
35
|
+
private formatFingerprintPart;
|
|
36
|
+
private getFingerprint;
|
|
37
|
+
private getModel;
|
|
38
|
+
private getSerialNumber;
|
|
39
|
+
private getCachedDeviceSerialNumber;
|
|
35
40
|
private applyAccessoryMetadata;
|
|
36
41
|
private ensureAccessoryInformationConfiguredName;
|
|
37
42
|
private pruneUnmanagedServices;
|
|
@@ -225,10 +225,59 @@ class ZWaveAccessory {
|
|
|
225
225
|
getEffectiveName() {
|
|
226
226
|
return this.platformAccessory.displayName || this.getDesiredName();
|
|
227
227
|
}
|
|
228
|
+
formatFingerprintPart(value) {
|
|
229
|
+
if (value === undefined) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
return `0x${value.toString(16).padStart(4, '0').toUpperCase()}`;
|
|
233
|
+
}
|
|
234
|
+
getFingerprint() {
|
|
235
|
+
const manufacturerId = this.formatFingerprintPart(this.node.manufacturerId);
|
|
236
|
+
const productType = this.formatFingerprintPart(this.node.productType);
|
|
237
|
+
const productId = this.formatFingerprintPart(this.node.productId);
|
|
238
|
+
if (!manufacturerId || !productType || !productId) {
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
return `${manufacturerId}:${productType}:${productId}`;
|
|
242
|
+
}
|
|
243
|
+
getModel() {
|
|
244
|
+
const fingerprint = this.getFingerprint();
|
|
245
|
+
const label = this.node.label || this.node.deviceConfig?.label;
|
|
246
|
+
if (label && fingerprint) {
|
|
247
|
+
return `${label} (${fingerprint})`;
|
|
248
|
+
}
|
|
249
|
+
return fingerprint || label || `Node ${this.node.nodeId}`;
|
|
250
|
+
}
|
|
251
|
+
getSerialNumber() {
|
|
252
|
+
const cachedSerialNumber = this.getCachedDeviceSerialNumber();
|
|
253
|
+
if (cachedSerialNumber) {
|
|
254
|
+
return cachedSerialNumber;
|
|
255
|
+
}
|
|
256
|
+
const fingerprint = this.getFingerprint();
|
|
257
|
+
if (fingerprint) {
|
|
258
|
+
return `zwave-${fingerprint}-node-${this.node.nodeId}`;
|
|
259
|
+
}
|
|
260
|
+
return `node-${this.node.nodeId}`;
|
|
261
|
+
}
|
|
262
|
+
getCachedDeviceSerialNumber() {
|
|
263
|
+
if (this.node.deviceSerialNumber) {
|
|
264
|
+
return this.node.deviceSerialNumber;
|
|
265
|
+
}
|
|
266
|
+
if (typeof this.node.getValue !== 'function') {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
const cachedValue = this.node.getValue({
|
|
270
|
+
commandClass: core_1.CommandClasses['Manufacturer Specific'],
|
|
271
|
+
endpoint: 0,
|
|
272
|
+
property: 'deviceId',
|
|
273
|
+
propertyKey: 'SerialNumber',
|
|
274
|
+
});
|
|
275
|
+
return typeof cachedValue === 'string' && cachedValue.length > 0 ? cachedValue : undefined;
|
|
276
|
+
}
|
|
228
277
|
applyAccessoryMetadata(options = {}) {
|
|
229
278
|
const manufacturer = this.node.manufacturer || this.node.deviceConfig?.manufacturer || 'Unknown';
|
|
230
|
-
const model = this.
|
|
231
|
-
const serial =
|
|
279
|
+
const model = this.getModel();
|
|
280
|
+
const serial = this.getSerialNumber();
|
|
232
281
|
const name = this.getEffectiveName();
|
|
233
282
|
const firmwareRevision = this.node.firmwareVersion || '1.0.0';
|
|
234
283
|
const metadataSignature = JSON.stringify({
|
|
@@ -36,6 +36,7 @@ export declare class ZWaveController extends EventEmitter implements IZWaveContr
|
|
|
36
36
|
private setupControllerListeners;
|
|
37
37
|
get homeId(): number | undefined;
|
|
38
38
|
private addNode;
|
|
39
|
+
private refreshNodeDeviceSerialNumber;
|
|
39
40
|
private removeNode;
|
|
40
41
|
/**
|
|
41
42
|
* Parses security keys from options into Buffer format for Z-Wave JS.
|
|
@@ -9,6 +9,7 @@ const core_1 = require("@zwave-js/core");
|
|
|
9
9
|
const events_1 = require("events");
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const MANUFACTURER_SPECIFIC_SERIAL_NUMBER_TYPE = 1;
|
|
12
13
|
/**
|
|
13
14
|
* ZWaveController wraps the Z-Wave JS Driver and implements high-level
|
|
14
15
|
* automation logic for inclusion, exclusion, and security PIN management.
|
|
@@ -161,6 +162,7 @@ class ZWaveController extends events_1.EventEmitter {
|
|
|
161
162
|
this.log.info(`Node ${node.nodeId} is ready (Interview Stage: ${node.interviewStage})`);
|
|
162
163
|
this.emit('status updated', `Node ${node.nodeId} Ready`);
|
|
163
164
|
this.emit('node ready', node);
|
|
165
|
+
void this.refreshNodeDeviceSerialNumber(node);
|
|
164
166
|
};
|
|
165
167
|
const onValueUpdated = (n, args) => {
|
|
166
168
|
this.log.debug(`Node ${n.nodeId} value updated`);
|
|
@@ -187,6 +189,7 @@ class ZWaveController extends events_1.EventEmitter {
|
|
|
187
189
|
this.log.info(`Node ${node.nodeId} has woken up. Interview will resume.`);
|
|
188
190
|
this.emit('status updated', `Node ${node.nodeId} Awake`);
|
|
189
191
|
this.emit('node updated', node);
|
|
192
|
+
void this.refreshNodeDeviceSerialNumber(node);
|
|
190
193
|
};
|
|
191
194
|
const onSleep = () => {
|
|
192
195
|
this.log.info(`Node ${node.nodeId} has gone to sleep. Interview is paused.`);
|
|
@@ -239,6 +242,35 @@ class ZWaveController extends events_1.EventEmitter {
|
|
|
239
242
|
if (node.ready) {
|
|
240
243
|
this.log.info(`Node ${node.nodeId} is already ready, emitting node ready immediately`);
|
|
241
244
|
this.emit('node ready', node);
|
|
245
|
+
void this.refreshNodeDeviceSerialNumber(node);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async refreshNodeDeviceSerialNumber(node) {
|
|
249
|
+
if (node.deviceSerialNumber) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (typeof node.supportsCC !== 'function' ||
|
|
253
|
+
!node.supportsCC(core_1.CommandClasses['Manufacturer Specific'])) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const manufacturerSpecificApi = node.commandClasses?.['Manufacturer Specific'];
|
|
257
|
+
if (typeof manufacturerSpecificApi?.deviceSpecificGet !== 'function') {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const deviceSerialNumber = await manufacturerSpecificApi.deviceSpecificGet(MANUFACTURER_SPECIFIC_SERIAL_NUMBER_TYPE);
|
|
262
|
+
if (typeof deviceSerialNumber !== 'string' || deviceSerialNumber.length === 0) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (node.deviceSerialNumber === deviceSerialNumber) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
node.deviceSerialNumber = deviceSerialNumber;
|
|
269
|
+
this.log.info(`Node ${node.nodeId} reported device serial number: ${deviceSerialNumber}`);
|
|
270
|
+
this.emit('node updated', node);
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
this.log.debug(`Node ${node.nodeId} did not provide a device serial number yet: ${err}`);
|
|
242
274
|
}
|
|
243
275
|
}
|
|
244
276
|
removeNode(node) {
|
|
@@ -50,6 +50,10 @@ export interface IZWaveNode {
|
|
|
50
50
|
name?: string;
|
|
51
51
|
manufacturer?: string;
|
|
52
52
|
label?: string;
|
|
53
|
+
deviceSerialNumber?: string;
|
|
54
|
+
manufacturerId?: number;
|
|
55
|
+
productType?: number;
|
|
56
|
+
productId?: number;
|
|
53
57
|
deviceConfig?: {
|
|
54
58
|
label?: string;
|
|
55
59
|
manufacturer?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-zwave-usb",
|
|
3
3
|
"displayName": "Homebridge Z-Wave USB",
|
|
4
|
-
"version": "3.6.
|
|
4
|
+
"version": "3.6.3",
|
|
5
5
|
"description": "A Homebridge dynamic platform plugin for Z-Wave USB controllers using zwave-js.",
|
|
6
6
|
"license": "Polyform-Noncommercial-1.0.0",
|
|
7
7
|
"funding": {
|