zwave-js
Version:
Z-Wave driver written entirely in JavaScript/TypeScript
183 lines • 6.03 kB
JavaScript
import { ZWaveError, ZWaveErrorCodes } from "@zwave-js/core";
import { Bytes } from "@zwave-js/shared";
export var AttachmentTypes;
(function (AttachmentTypes) {
AttachmentTypes[AttachmentTypes["NetworkKeys"] = 3] = "NetworkKeys";
AttachmentTypes[AttachmentTypes["FrameComment"] = 4] = "FrameComment";
AttachmentTypes[AttachmentTypes["Session"] = 5] = "Session";
})(AttachmentTypes || (AttachmentTypes = {}));
export class ZLFAttachmentRaw {
type;
version;
data;
constructor(type, version, data) {
this.type = type;
this.version = version;
this.data = data;
}
static parse(buffer) {
if (buffer.length < 2) {
throw new ZWaveError("Incomplete ZLF attachment", ZWaveErrorCodes.PacketFormat_Truncated);
}
const type = buffer[0];
const version = buffer[1];
// The remaining bytes are the data
const data = Bytes.view(buffer.subarray(2));
return {
raw: new ZLFAttachmentRaw(type, version, data),
bytesRead: buffer.length,
};
}
withData(data) {
return new ZLFAttachmentRaw(this.type, this.version, data);
}
}
/**
* Retrieves the correct constructor for the attachment based on its type.
*/
function getZLFAttachmentConstructor(raw) {
switch (raw.type) {
case AttachmentTypes.NetworkKeys:
return ZLFNetworkKeysAttachment;
case AttachmentTypes.FrameComment:
return ZLFFrameCommentAttachment;
case AttachmentTypes.Session:
return ZLFSessionAttachment;
default:
return ZLFAttachment;
}
}
/**
* Represents a ZLF attachment for parsing data from a Zniffer trace
*/
export class ZLFAttachment {
constructor(options) {
this.type = options.type;
this.version = options.version;
this.data = options.data || new Bytes();
}
static parse(buffer) {
const { raw, bytesRead } = ZLFAttachmentRaw.parse(buffer);
const Constructor = getZLFAttachmentConstructor(raw);
return {
attachment: Constructor.from(raw),
bytesRead,
};
}
/** Creates an instance of the attachment that is serialized in the given buffer */
static from(raw) {
return new this({
type: raw.type,
version: raw.version,
data: raw.data,
});
}
type;
version;
data;
/** Serializes this attachment into a Buffer */
serialize() {
return Bytes.concat([
Bytes.from([this.type, this.version]),
this.data,
]);
}
}
export class ZLFNetworkKeysAttachment extends ZLFAttachment {
constructor(options) {
super({
type: AttachmentTypes.NetworkKeys,
version: 1,
});
this.homeId = options.homeId;
this.keys = options.keys.map((key) => Bytes.from(key));
this.tempKeys = options.tempKeys.map((key) => Bytes.from(key));
}
static from(raw) {
// This attachment consists of at least 2x4 home ID bytes,
// 2x1 byte count and 2x1 byte flags, plus 16 bytes per key
if (raw.data.length < 12) {
throw new ZWaveError("Incomplete NetworkKeysAttachment", ZWaveErrorCodes.PacketFormat_Truncated);
}
let offset = 0;
// Home ID seems to not be populated by the Zniffer software,
// but we'll parse it anyways
const homeId = raw.data.readUInt32BE(offset);
offset += 4;
const tempKeys = [];
const keys = [];
// Read first set of keys
let count = raw.data[offset++];
let isTemporary = !!(raw.data[offset++] & 0x01);
if (offset + count * 16 + 6 >= raw.data.length) {
throw new ZWaveError("Incomplete NetworkKeysAttachment", ZWaveErrorCodes.PacketFormat_Truncated);
}
for (let i = 0; i < count; i++) {
const key = raw.data.subarray(offset, offset + 16);
offset += 16;
if (isTemporary) {
tempKeys.push(key);
}
else {
keys.push(key);
}
}
// Read second set of keys
// Skip homeID bytes
offset += 4;
count = raw.data[offset++];
isTemporary = !!(raw.data[offset++] & 0x01);
if (offset + count * 16 > raw.data.length) {
throw new ZWaveError("Incomplete NetworkKeysAttachment", ZWaveErrorCodes.PacketFormat_Truncated);
}
for (let i = 0; i < count; i++) {
const key = raw.data.subarray(offset, offset + 16);
offset += 16;
if (isTemporary) {
tempKeys.push(key);
}
else {
keys.push(key);
}
}
return new ZLFNetworkKeysAttachment({
homeId,
keys,
tempKeys,
});
}
homeId;
keys;
tempKeys;
}
export class ZLFFrameCommentAttachment extends ZLFAttachment {
constructor(options) {
super({
type: AttachmentTypes.FrameComment,
version: options.version || 1,
data: options.data,
});
this.comment = options.comment;
}
static from(_raw) {
// TODO: Implement parsing logic
throw new ZWaveError("ZLFFrameCommentAttachment parsing not yet implemented", ZWaveErrorCodes.Deserialization_NotImplemented);
}
comment;
}
export class ZLFSessionAttachment extends ZLFAttachment {
constructor(options) {
super({
type: AttachmentTypes.Session,
version: options.version || 1,
data: options.data,
});
this.sessionId = options.sessionId;
}
static from(_raw) {
// TODO: Implement parsing logic
throw new ZWaveError("ZLFSessionAttachment parsing not yet implemented", ZWaveErrorCodes.Deserialization_NotImplemented);
}
sessionId;
}
//# sourceMappingURL=ZLFAttachment.js.map