Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Move ZclFrame.fromBuffer() out of adapter code #1011

Merged
merged 18 commits into from
Apr 15, 2024
80 changes: 38 additions & 42 deletions src/adapter/deconz/adapter/deconzAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@
/* eslint-disable */
import {
NetworkOptions, SerialPortOptions, Coordinator, CoordinatorVersion, NodeDescriptor,
DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, Backup as BackupType, NetworkParameters,
DeviceType, ActiveEndpoints, SimpleDescriptor, LQI, RoutingTable, NetworkParameters,
StartResult, LQINeighbor, RoutingTableEntry, AdapterOptions,
} from '../../tstype';
import Adapter from '../../adapter';
import Driver from '../driver/driver';
import {ZclFrame, FrameType, Direction, Foundation} from '../../../zcl';
import {ZclFrame, FrameType, Direction, ZclHeader} from '../../../zcl';
import * as Events from '../../events';
import * as Zcl from '../../../zcl';
import {GreenPowerEvents, GreenPowerDeviceJoinedPayload} from '../../../controller/tstype';
import processFrame from '../driver/frameParser';
import {Queue, Waitress, Wait} from '../../../utils';
import PARAM from '../driver/constants';
import { Command, WaitForDataRequest, ApsDataRequest, ReceivedDataResponse, DataStateResponse, gpDataInd } from '../driver/constants';
import { WaitForDataRequest, ApsDataRequest, ReceivedDataResponse, gpDataInd } from '../driver/constants';
import * as Models from "../../../models";
import {logger} from '../../../utils/logger';

Expand Down Expand Up @@ -642,11 +640,12 @@ class DeconzAdapter extends Adapter {
if (data !== null) {
const asdu = data.asduPayload;
const buffer = Buffer.from(asdu);
const frame: ZclFrame = ZclFrame.fromBuffer(zclFrame.Cluster.ID, buffer);

const response: Events.ZclDataPayload = {
address: (data.srcAddrMode === 0x02) ? data.srcAddr16 : null,
frame: frame,
data: buffer,
clusterID: zclFrame.Cluster.ID,
zclFrameHeader: ZclHeader.fromBuffer(buffer),
endpoint: data.srcEndpoint,
linkquality: data.lqi,
groupID: (data.srcAddrMode === 0x01) ? data.srcAddr16 : null,
Expand Down Expand Up @@ -1084,7 +1083,9 @@ class DeconzAdapter extends Adapter {

const payBuf = Buffer.from(gpFrame);
const payload: Events.ZclDataPayload = {
frame: ZclFrame.fromBuffer(ind.clusterId, payBuf),
zclFrameHeader: ZclHeader.fromBuffer(payBuf),
data: payBuf,
clusterID: ind.clusterId,
address: ind.srcId,
endpoint: 242, // GP endpoint
linkquality: 127,
Expand All @@ -1094,18 +1095,18 @@ class DeconzAdapter extends Adapter {
};

this.waitress.resolve(payload);
this.emit(Events.Events.zclData, payload);
this.emit(Events.Events.data, payload);
}

private checkReceivedDataPayload(resp: ReceivedDataResponse) {
let srcAddr: any = null;
let frame: ZclFrame = null;
let header: ZclHeader = null;

if (resp != null) {
srcAddr = (resp.srcAddr16 != null) ? resp.srcAddr16 : resp.srcAddr64;
if (resp.profileId != 0x00) {
try {
frame = ZclFrame.fromBuffer(resp.clusterId, Buffer.from(resp.asduPayload));
header = ZclHeader.fromBuffer(Buffer.from(resp.asduPayload));
} catch (error) {
logger.debug("could not parse zclFrame: " + error, NS);
}
Expand All @@ -1117,8 +1118,8 @@ class DeconzAdapter extends Adapter {
const req: WaitForDataRequest = this.openRequestsQueue[i];
if (srcAddr != null && req.addr === srcAddr && req.clusterId === resp.clusterId &&
req.profileId === resp.profileId) {
if (frame !== null && req.transactionSequenceNumber !== null && req.transactionSequenceNumber !== undefined) {
if (req.transactionSequenceNumber === frame.Header.transactionSequenceNumber) {
if (header !== null && req.transactionSequenceNumber !== null && req.transactionSequenceNumber !== undefined) {
if (req.transactionSequenceNumber === header.transactionSequenceNumber) {
logger.debug("resolve data request with transSeq Nr.: " + req.transactionSequenceNumber, NS);
this.openRequestsQueue.splice(i, 1);
req.resolve(resp);
Expand Down Expand Up @@ -1159,33 +1160,27 @@ class DeconzAdapter extends Adapter {

if (resp != null && resp.profileId != 0x00) {
const payBuf = Buffer.from(resp.asduPayload);
let header: ZclHeader = undefined;
try {
const payload: Events.ZclDataPayload = {
frame: ZclFrame.fromBuffer(resp.clusterId, payBuf),
address: (resp.destAddrMode === 0x03) ? resp.srcAddr64 : resp.srcAddr16,
endpoint: resp.srcEndpoint,
linkquality: resp.lqi,
groupID: (resp.destAddrMode === 0x01) ? resp.destAddr16 : null,
wasBroadcast: resp.destAddrMode === 0x01 || resp.destAddrMode === 0xF,
destinationEndpoint: resp.destEndpoint,
};

this.waitress.resolve(payload);
this.emit(Events.Events.zclData, payload);
header = ZclHeader.fromBuffer(payBuf);
} catch (error) {
const payload: Events.RawDataPayload = {
clusterID: resp.clusterId,
data: payBuf,
address: (resp.destAddrMode === 0x03) ? resp.srcAddr64 : resp.srcAddr16,
endpoint: resp.srcEndpoint,
linkquality: resp.lqi,
groupID: (resp.destAddrMode === 0x01) ? resp.destAddr16 : null,
wasBroadcast: resp.destAddrMode === 0x01 || resp.destAddrMode === 0xF,
destinationEndpoint: resp.destEndpoint,
};

this.emit(Events.Events.rawData, payload);
logger.debug(`Failed to parse header: ${error}`, NS);
}

const payload: Events.ZclDataPayload = {
clusterID: resp.clusterId,
zclFrameHeader: header,
data: payBuf,
address: (resp.destAddrMode === 0x03) ? resp.srcAddr64 : resp.srcAddr16,
endpoint: resp.srcEndpoint,
linkquality: resp.lqi,
groupID: (resp.destAddrMode === 0x01) ? resp.destAddr16 : null,
wasBroadcast: resp.destAddrMode === 0x01 || resp.destAddrMode === 0xF,
destinationEndpoint: resp.destEndpoint,
};

this.waitress.resolve(payload);
this.emit(Events.Events.data, payload);
}
}

Expand All @@ -1206,14 +1201,15 @@ class DeconzAdapter extends Adapter {
}

private waitressValidator(payload: Events.ZclDataPayload, matcher: WaitressMatcher): boolean {
const transactionSequenceNumber = payload.frame.Header.transactionSequenceNumber;
if (!payload.zclFrameHeader) return false;
const transactionSequenceNumber = payload.zclFrameHeader.transactionSequenceNumber;
return (!matcher.address || payload.address === matcher.address) &&
payload.endpoint === matcher.endpoint &&
(!matcher.transactionSequenceNumber || transactionSequenceNumber === matcher.transactionSequenceNumber) &&
payload.frame.Cluster.ID === matcher.clusterID &&
matcher.frameType === payload.frame.Header.frameControl.frameType &&
matcher.commandIdentifier === payload.frame.Header.commandIdentifier &&
matcher.direction === payload.frame.Header.frameControl.direction;
payload.clusterID === matcher.clusterID &&
matcher.frameType === payload.zclFrameHeader.frameControl.frameType &&
matcher.commandIdentifier === payload.zclFrameHeader.commandIdentifier &&
matcher.direction === payload.zclFrameHeader.frameControl.direction;
}
}

Expand Down
63 changes: 30 additions & 33 deletions src/adapter/ember/adapter/emberAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ import SocketPortUtils from '../../socketPortUtils';
import {BackupUtils, RealpathSync, Wait} from "../../../utils";
import {Adapter, TsType} from "../..";
import {Backup, UnifiedBackupStorage} from "../../../models";
import {FrameType, Direction, ZclFrame, Foundation, ManufacturerCode} from "../../../zcl";
import {FrameType, Direction, ZclFrame, ZclHeader, Foundation, ManufacturerCode} from "../../../zcl";
import Cluster from "../../../zcl/definition/cluster";
import {
DeviceAnnouncePayload,
DeviceJoinedPayload,
DeviceLeavePayload,
Events,
RawDataPayload,
ZclDataPayload
} from "../../events";
import {halCommonCrc16, highByte, highLowToInt, lowByte, lowHighBytes} from "../utils/math";
Expand Down Expand Up @@ -601,33 +600,27 @@ export class EmberAdapter extends Adapter {
*/
private async onIncomingMessage(type: EmberIncomingMessageType, apsFrame: EmberApsFrame, lastHopLqi: number, sender: EmberNodeId,
messageContents: Buffer): Promise<void> {
let header: ZclHeader = undefined;
try {
const payload: ZclDataPayload = {
address: sender,
frame: ZclFrame.fromBuffer(apsFrame.clusterId, messageContents),
endpoint: apsFrame.sourceEndpoint,
linkquality: lastHopLqi,
groupID: apsFrame.groupId,
wasBroadcast: ((type === EmberIncomingMessageType.BROADCAST) || (type === EmberIncomingMessageType.BROADCAST_LOOPBACK)),
destinationEndpoint: apsFrame.destinationEndpoint,
};

this.oneWaitress.resolveZCL(payload);
this.emit(Events.zclData, payload);
header = ZclHeader.fromBuffer(messageContents);
} catch (error) {
const payload: RawDataPayload = {
clusterID: apsFrame.clusterId,
address: sender,
data: messageContents,
endpoint: apsFrame.sourceEndpoint,
linkquality: lastHopLqi,
groupID: apsFrame.groupId,
wasBroadcast: ((type === EmberIncomingMessageType.BROADCAST) || (type === EmberIncomingMessageType.BROADCAST_LOOPBACK)),
destinationEndpoint: apsFrame.destinationEndpoint,
};

this.emit(Events.rawData, payload);
logger.debug(`Failed to parse header: ${error}`, NS);
}

const payload: ZclDataPayload = {
clusterID: apsFrame.clusterId,
zclFrameHeader: header,
address: sender,
data: messageContents,
endpoint: apsFrame.sourceEndpoint,
linkquality: lastHopLqi,
groupID: apsFrame.groupId,
wasBroadcast: ((type === EmberIncomingMessageType.BROADCAST) || (type === EmberIncomingMessageType.BROADCAST_LOOPBACK)),
destinationEndpoint: apsFrame.destinationEndpoint,
};

this.oneWaitress.resolveZCL(payload);
this.emit(Events.data, payload);
}

/**
Expand All @@ -642,7 +635,9 @@ export class EmberAdapter extends Adapter {
private async onTouchlinkMessage(sourcePanId: EmberPanId, sourceAddress: EmberEUI64, groupId: number | null, lastHopLqi: number,
messageContents: Buffer): Promise<void> {
const payload: ZclDataPayload = {
frame: ZclFrame.fromBuffer(Cluster.touchlink.ID, messageContents),
clusterID: Cluster.touchlink.ID,
data: messageContents,
zclFrameHeader: ZclHeader.fromBuffer(messageContents),
address: sourceAddress,
endpoint: 1,// arbitrary since not sent over-the-air
linkquality: lastHopLqi,
Expand All @@ -652,7 +647,7 @@ export class EmberAdapter extends Adapter {
};

this.oneWaitress.resolveZCL(payload);
this.emit(Events.zclData, payload);
this.emit(Events.data, payload);
}

/**
Expand Down Expand Up @@ -681,20 +676,22 @@ export class EmberAdapter extends Adapter {
gpdHeader.writeUInt8(gpdCommandId, 13);// commandID
gpdHeader.writeUInt8(gpdCommandPayload.length, 14);// payloadSize

const gpFrame = ZclFrame.fromBuffer(Cluster.greenPower.ID, Buffer.concat([gpdHeader, gpdCommandPayload]));
const data = Buffer.concat([gpdHeader, gpdCommandPayload]);
const header = ZclHeader.fromBuffer(data);
const payload: ZclDataPayload = {
frame: gpFrame,
zclFrameHeader: header,
data,
clusterID: Cluster.greenPower.ID,
address: sourceId,
endpoint: GP_ENDPOINT,
linkquality: gpdLink,
groupID: this.greenPowerGroup,
// XXX: upstream sends to `gppNwkAddr` if `wasBroadcast` is false, even if `gppNwkAddr` is null
wasBroadcast: (gpFrame.Payload.gppNwkAddr != null) ? false : true,
wasBroadcast: true,
destinationEndpoint: GP_ENDPOINT,
};

this.oneWaitress.resolveZCL(payload);
this.emit(Events.zclData, payload);
this.emit(Events.data, payload);
} catch (err) {
logger.error(`<~x~ [GP] Failed creating ZCL payload. Skipping. ${err}`, NS);
return;
Expand Down
8 changes: 5 additions & 3 deletions src/adapter/ember/adapter/oneWaitress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ export class EmberOneWaitress {
}

public resolveZCL(payload: ZclDataPayload): boolean {
if (!payload.zclFrameHeader) return false;

for (const [index, waiter] of this.waiters.entries()) {
if (waiter.timedout) {
this.waiters.delete(index);
Expand All @@ -155,9 +157,9 @@ export class EmberOneWaitress {

// no target in touchlink, also no APS sequence, but use the ZCL one instead
if (((waiter.matcher.apsFrame.profileId === TOUCHLINK_PROFILE_ID) || (payload.address === waiter.matcher.target))
&& (!waiter.matcher.zclSequence || (payload.frame.Header.transactionSequenceNumber === waiter.matcher.zclSequence))
&& (!waiter.matcher.commandIdentifier || (payload.frame.Header.commandIdentifier === waiter.matcher.commandIdentifier))
&& (payload.frame.Cluster.ID === waiter.matcher.apsFrame.clusterId)
&& (!waiter.matcher.zclSequence || (payload.zclFrameHeader.transactionSequenceNumber === waiter.matcher.zclSequence))
&& (!waiter.matcher.commandIdentifier || (payload.zclFrameHeader.commandIdentifier === waiter.matcher.commandIdentifier))
&& (payload.clusterID === waiter.matcher.apsFrame.clusterId)
&& (payload.endpoint === waiter.matcher.apsFrame.destinationEndpoint)) {
clearTimeout(waiter.timer);

Expand Down
18 changes: 4 additions & 14 deletions src/adapter/events.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {ZclFrame} from '../zcl';
import {ZclHeader} from '../zcl';

enum Events {
networkAddress = "networkAddress",
deviceJoined = "deviceJoined",
zclData = "zclData",
rawData = "rawData",
data = "data",
disconnected = "disconnected",
deviceAnnounce = "deviceAnnounce",
deviceLeave = "deviceLeave"
Expand Down Expand Up @@ -34,18 +33,10 @@ type DeviceLeavePayload = {
};

interface ZclDataPayload {
address: number | string;
frame: ZclFrame;
endpoint: number;
linkquality: number;
groupID: number;
wasBroadcast: boolean;
destinationEndpoint: number;
}

interface RawDataPayload {
clusterID: number;
address: number | string;
zclFrameHeader: ZclHeader | undefined;
// This buffer contains the whole ZclFrame (including the ZclHeader)
data: Buffer;
endpoint: number;
linkquality: number;
Expand All @@ -56,5 +47,4 @@ interface RawDataPayload {

export {
Events, DeviceJoinedPayload, ZclDataPayload, DeviceAnnouncePayload, NetworkAddressPayload, DeviceLeavePayload,
RawDataPayload,
};
Loading
Loading