Skip to content

Commit

Permalink
Updated zclFrameToBuffer (Koenkk#177)
Browse files Browse the repository at this point in the history
* Don't throw promise.reject() when permit joins fails. But try again.

* Set network paramters according to configuration.yaml

* Speed up command queues

* Retry reset watchdog if it fails once

* Force RaspBee and ConBee to use 16bit NWK addr in data indications

* Fix wrong handling of 64bit src addresses in received data responses

* Remove unnecessary debug output

* Use given zclFrame.toBuffer() function instead of own implementation

* Don't throw error when unknown parameter is received

* Added writeparameter request to permit join request

* Removed unnecessary console.log

* Start on conbee

* Updates.

* conbee -> deconz. Koenkk/zigbee-herdsman#72

* Initial suppport for reading parameters

- Add basic frame parser
- Handle slip protocol and crc calculation
- Add queue for read and write parameters

* Add dummy function DeconzAdapter.supportsLED()

* Start working on write parameters

* supportsLED() returns false

* Implemented getCoordinator()

* Added deCONZ adapter dummy functions

* Implemented getCoordinatorVersion()

* Implemented getNetworkParameters()

* Added constants for APS layer

* Startet APS layer

Added readReceivedStateRequest, deviceStateRequest, deviceStateResponse,
readReceivedDataRequest

* Added APS layer parser functions

parseDeviceStateResponse, parseReadReceivedDataResponse, parseReceivedDataNotification,

* Added parser for APS responses

* Refactored some deCONZ adapter functions

* Added sendZclFrameNetworkAddress, sendZclFrameNetworkAddressWithResponse and sendZclFrameGroup

* Added constants for deCONZ adapter

* Added deCONZ driver functions for sending APS commands

* Added discoverRoute, supportsDiscoverRoute dummy functions

* Implemented NodeDescriptor

* Added constants

* Reworked debug messages and fixed send unnecessary device state requests

* Some smal fixes

* Implemented permit join

* Fixed check DeviceStatus variables

* implemented Active Endpoint request

* Implemented simple descriptor

* Removed reading request_id if data confirm fail

* Fixed reading payload of data indication

* Fix nodedescriptor devicetype

* Added waitFor dummy function

* Implemented lqi()

* Implemented routingTable()

* implemented bind()

* implmented unbind()

* Implemented removeDevice()

* Changes at driver.ts

* Fix for resolve aps request error

* ieeeAddr to String now add '0x' prefix

* Get correct APS data payload

* Fixes for some adapter functions

* Delete old code

* Added constant

* Added support of timeout for aps requests

* Refactored deCONZ adapter functions

* Added constants

* Refactored driver functions and debug logs

* Refactored frameparser debug logs

* Updated deCONZ adapter to newest adapter version

* Added device state intervall wand tweaked watchdog

* Removed unused code

* Removed unused code

* Added received msg event, removed discoverRoute

* Added catch for devicestaterequest

* Handle default response when sending zcl message

* Refactored debug print

* Added /* istanbul ignore file */

* Fixed/ implemented zclFrame.payload to Array (for dim and color commands)

* Implemented waitFor() and some changes for OTA update

* Update deconzAdapter.ts

* Fixed sending manufacturere specific commands

* Added some attributes to zclpayloadToArray conversion needed for e.g. configure reporting

TODO: this has to be reworked completely

* Fixed zclPayload to array function (used by configureReporting)

handle arrays of objects correctly

Co-authored-by: Koen Kanters <[email protected]>
  • Loading branch information
ChrisHae and Koenkk authored May 12, 2020
1 parent 9681df0 commit 390025f
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 191 deletions.
234 changes: 44 additions & 190 deletions src/adapter/deconz/adapter/deconzAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,53 +518,12 @@ class DeconzAdapter extends Adapter {
): Promise<Events.ZclDataPayload> {
const transactionID = this.nextTransactionID();
const request: ApsDataRequest = {};
let frameControl: string = "";
frameControl += (0);
frameControl += (0);
frameControl += (0);
frameControl += ((zclFrame.Header.frameControl.disableDefaultResponse) ? 1 : 0);
frameControl += (zclFrame.Header.frameControl.direction);
frameControl += ((zclFrame.Header.frameControl.manufacturerSpecific) ? 1 : 0);
frameControl += (0);
frameControl += (zclFrame.Header.frameControl.frameType);
let payload = [];
if (zclFrame.Header.frameControl.manufacturerSpecific === true) {
const manf1 = zclFrame.Header.manufacturerCode & 0xff;
const manf2 = (zclFrame.Header.manufacturerCode >> 8) & 0xff;
payload = [parseInt(frameControl,2), manf1, manf2, zclFrame.Header.transactionSequenceNumber, zclFrame.Header.commandIdentifier];
} else {
payload = [parseInt(frameControl,2), zclFrame.Header.transactionSequenceNumber, zclFrame.Header.commandIdentifier];
}
//console.log("send zclframe to endpoint: zclFrame");
//console.log(zclFrame);
//console.log("zclFrame.payload:");
//console.log(zclFrame.Payload);
let pay: number[] = [];
let isRawData = false;

if ((typeof zclFrame.Payload) === 'object') {
if (Array.isArray(zclFrame.Payload)) {
for (let i in zclFrame.Payload) {
let entry = zclFrame.Payload[i];
if ((typeof entry) === 'object') {
// payload is array of objects
pay = pay.concat(this.zclPayloadToArray(entry));
} else {
// payload is array of raw data
isRawData = true;
payload.push(entry);
}
}
if (isRawData) {
pay = payload;
} else {
pay = payload.concat(pay);
}
} else {
// payload is object
pay = payload.concat(this.zclPayloadToArray(zclFrame.Payload));
}
}
let pay = zclFrame.toBuffer();

debug("zclFrame.payload:");
debug(zclFrame.Payload);
//console.log("zclFramte.toBuffer:");
//console.log(pay);

request.requestId = transactionID;
request.destAddrMode = PARAM.PARAM.addressMode.NWK_ADDR;
Expand All @@ -574,7 +533,7 @@ class DeconzAdapter extends Adapter {
request.clusterId = zclFrame.Cluster.ID;
request.srcEndpoint = 1;
request.asduLength = pay.length;
request.asduPayload = pay;
request.asduPayload = [... pay];
request.txOptions = 0;
request.radius = PARAM.PARAM.txRadius.DEFAULT_RADIUS;
request.timeout = timeout;
Expand Down Expand Up @@ -628,50 +587,12 @@ class DeconzAdapter extends Adapter {
public async sendZclFrameToGroup(groupID: number, zclFrame: ZclFrame): Promise<void> {
const transactionID = this.nextTransactionID();
const request: ApsDataRequest = {};
let frameControl: string = "";
frameControl += (0);
frameControl += (0);
frameControl += (0);
frameControl += ((zclFrame.Header.frameControl.disableDefaultResponse) ? 1 : 0);
frameControl += (zclFrame.Header.frameControl.direction);
frameControl += ((zclFrame.Header.frameControl.manufacturerSpecific) ? 1 : 0);
frameControl += (0);
frameControl += (zclFrame.Header.frameControl.frameType);
let payload = [];
if (zclFrame.Header.frameControl.manufacturerSpecific === true) {
const manf1 = zclFrame.Header.manufacturerCode & 0xff;
const manf2 = (zclFrame.Header.manufacturerCode >> 8) & 0xff;
payload = [parseInt(frameControl,2), manf1, manf2, zclFrame.Header.transactionSequenceNumber, zclFrame.Header.commandIdentifier];
} else {
payload = [parseInt(frameControl,2), zclFrame.Header.transactionSequenceNumber, zclFrame.Header.commandIdentifier];
}
let pay = zclFrame.toBuffer();

let pay: number[] = [];
let isRawData = false;
if ((typeof zclFrame.Payload) === 'object') {
if (Array.isArray(zclFrame.Payload)) {
for (let i in zclFrame.Payload) {
let entry = zclFrame.Payload[i];
if ((typeof entry) === 'object') {
// payload is array of objects
pay = pay.concat(this.zclPayloadToArray(entry));
} else {
// payload is array of raw data
isRawData = true;
payload.push(entry);
}
}
if (isRawData) {

pay = payload;
} else {
pay = payload.concat(pay);
}
} else {
// payload is object
pay = payload.concat(this.zclPayloadToArray(zclFrame.Payload));
}
}
debug("zclFrame to group - zclFrame.payload:");
debug(zclFrame.Payload);
//console.log("zclFramte.toBuffer:");
//console.log(pay);

request.requestId = transactionID;
request.destAddrMode = PARAM.PARAM.addressMode.GROUP_ADDR;
Expand All @@ -680,11 +601,12 @@ class DeconzAdapter extends Adapter {
request.clusterId = zclFrame.Cluster.ID;
request.srcEndpoint = 1;
request.asduLength = pay.length;
request.asduPayload = pay;
request.asduPayload = [... pay];
request.txOptions = 0;
request.radius = PARAM.PARAM.txRadius.UNLIMITED;

try {
debug(`sendZclFrameToGroup - message send`);
return this.driver.enqueueSendDataRequest(request) as Promise<void>;
} catch (error) {
debug(`sendZclFrameToGroup ERROR: ${error}`);
Expand All @@ -693,7 +615,36 @@ class DeconzAdapter extends Adapter {
}

public async sendZclFrameToAll(endpoint: number, zclFrame: ZclFrame, sourceEndpoint: number): Promise<void> {
// TODO: not implemented yet
return;
//TODO: does not work yet (e.g. after permit join)
const transactionID = this.nextTransactionID();
const request: ApsDataRequest = {};
let pay = zclFrame.toBuffer();

debug("zclFrame to all - zclFrame.payload:");
debug(zclFrame.Payload);
//console.log("zclFramte.toBuffer:");
//console.log(pay);

request.requestId = transactionID;
request.destAddrMode = PARAM.PARAM.addressMode.GROUP_ADDR;
request.destAddr16 = 0xFFFD;
request.destEndpoint = endpoint;
request.profileId = 0x104;
request.clusterId = zclFrame.Cluster.ID;
request.srcEndpoint = sourceEndpoint;
request.asduLength = pay.length;
request.asduPayload = [... pay];
request.txOptions = 0;
request.radius = PARAM.PARAM.txRadius.UNLIMITED;

try {
debug(`sendZclFrameToAll - message send`);
return this.driver.enqueueSendDataRequest(request) as Promise<void>;
} catch (error) {
debug(`sendZclFrameToAll ERROR: ${error}`);
return Promise.reject();
}
}

public async bind(
Expand Down Expand Up @@ -1062,103 +1013,6 @@ class DeconzAdapter extends Adapter {
}
}

private zclPayloadToArray(payload: Object): Array<number> {
let buf = Buffer.alloc(100); // zclPayload could be bigger ?
let offset = 0;
if ('colorx' in payload) {
// move to color
buf.writeUInt16LE(payload['colorx'], 0);
buf.writeUInt16LE(payload['colory'], 2);
buf.writeUInt16LE(payload['transtime'], 4);
offset = 6;
} else if ('enhancehue' in payload) {
// move to ext hue and saturation
buf.writeUInt16LE(payload['enhancehue'], 0);
buf.writeUInt8(payload['saturation'], 2);
buf.writeUInt16LE(payload['transtime'], 3);
offset = 5;
} else if ('attrId' in payload && 'attrData' in payload && 'dataType' in payload) {
// Report attributes command
buf.writeUInt16LE(payload['attrId'], 0);
buf.writeUInt8(payload['dataType'], 2);
offset += 3;
if (payload['dataType'] === 0x42) {
//string
const string = payload['attrData'];
const l = Buffer.byteLength(string);
for (let i = 0; i < l; i++) {
buf.writeUInt8(string[i], offset);
offset++;
}
} else if (payload['dataType'] === 0x19) {
//16bitmap
buf.writeUInt16LE(payload['attrData'], offset);
offset += 2;
} else {
//number
if (payload['attrData'] <= 0xFF) {
buf.writeUInt8(payload['attrData'], offset);
offset++;
} else if (payload['attrData'] <= 0xFFFF) {
buf.writeUInt16LE(payload['attrData'], offset);
offset += 2;
} else if (payload['attrData'] <= 0xFFFFFF) {
buf.writeUInt32LE(payload['attrData'], offset);
offset += 4;
}
}
} else {
for (let [key, value] of Object.entries(payload)) {
debug(`${key}: ${value}`);
switch (key) {
case "level": case "cmdId": case "statusCode": case "effectid": case "effectvariant":
case "saturation": case "direction": case "payloadType": case "queryJitter": case "status":
case "dataSize": case "dataType":
buf.writeUInt8(value, offset);
offset++;
break;
case "transtime": case "colortemp": case "manufacturerCode": case "imageType": case "attrId":
case "minRepIntval": case "maxRepIntval": case "timeout": case "groupid":
buf.writeUInt16LE(value, offset);
offset += 2;
break;
case "fileVersion": case "imageSize": case "fileOffset":
buf.writeUInt32LE(value, offset);
offset += 4;
break;
case "data": case "groupname":
const l = Buffer.byteLength(value);
for (let i = 0; i < l; i++) {
buf.writeUInt8(value[i], offset);
offset++;
}
break;
case "repChange": // todo extra if for this case with offset length determined by datatype
// see configure reporting
if (value <= 0xFF) {
buf.writeUInt8(value, offset);
offset++;
break;
} else if (value <= 0xFFFF) {
buf.writeUInt16LE(value, offset);
offset += 2;
break;
} else if (value <= 0xFFFFFF) {
buf.writeUInt32LE(value, offset);
offset += 4;
break;
}
default:
debug("zclPayloadToArray not implemented for key: " + key);
break;
}
}
}

let ret = [...buf].splice(0, offset);
return ret;
}

private nextTransactionID(): number {
this.transactionID++;

Expand Down
4 changes: 3 additions & 1 deletion src/adapter/deconz/driver/frameParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ function parseReadParameterResponse(view: DataView) : Command {
debug('WATCHDOG_TTL: ' + ttl);
return ttl;
default:
throw new Error("unknown parameter id");
//throw new Error(`unknown parameter id ${parameterId}`);
debug(`unknown parameter id ${parameterId}`);
return null;
}
}

Expand Down

0 comments on commit 390025f

Please sign in to comment.