Skip to content

Commit

Permalink
First working green power implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Koenkk committed Mar 29, 2020
1 parent 097d56e commit 7b81483
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ abstract class Adapter extends events.EventEmitter {

public abstract simpleDescriptor(networkAddress: number, endpointID: number): Promise<TsType.SimpleDescriptor>;

public abstract sendZclFrameNetworkAddressWithResponse(
networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, defaultResponseTimeout: number,
): Promise<ZclDataPayload>;

public abstract sendZclFrameNetworkAddress(
networkAddress: number, endpoint: number, zclFrame: ZclFrame, timeout: number, defaultResponseTimeout: number,
): Promise<void>;

public abstract sendZclFrameGroup(groupID: number, zclFrame: ZclFrame, timeout: number): Promise<void>;

public abstract sendZclFrameBroadcast(zclFrame: ZclFrame): Promise<void>;

public abstract bind(
destinationNetworkAddress: number, sourceIeeeAddress: string, sourceEndpoint: number,
clusterID: number, destinationAddressOrGroup: string | number, type: 'endpoint' | 'group',
Expand Down
1 change: 1 addition & 0 deletions src/adapter/z-stack/adapter/startZnp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const Endpoints = [
// Insta/Jung/Gira: OTA fallback EP (since it's buggy in firmware 10023202 when it tries to find a matching EP for
// OTA - it queries for ZLL profile, but then contacts with HA profile)
{...EndpointDefaults, endpoint: 47, appprofid: 0x0104},
{...EndpointDefaults, endpoint: 242, appprofid: 0xa1e0},
];

async function validateItem(
Expand Down
13 changes: 13 additions & 0 deletions src/adapter/z-stack/adapter/zStackAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,19 @@ class ZStackAdapter extends Adapter {
});
}

public async sendZclFrameBroadcast(zclFrame: ZclFrame): Promise<void> {
return this.queue.execute<void>(async () => {
await this.dataRequestExtended(Constants.COMMON.addressMode.ADDR_16BIT, 0xFFFD, 242, 0, 242, zclFrame.Cluster.ID, Constants.AF.DEFAULT_RADIUS, zclFrame.toBuffer(), 6000, false, 0)

/**
* As a group command is not confirmed and thus immidiately returns
* (contrary to network address requests) we will give the
* command some time to 'settle' in the network.
*/
await Wait(200);
});
}

public async lqi(networkAddress: number): Promise<LQI> {
return this.queue.execute<LQI>(async (): Promise<LQI> => {
const neighbors: LQINeighbor[] = [];
Expand Down
13 changes: 13 additions & 0 deletions src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Debug from "debug";
import fs from 'fs';
import {Utils as ZclUtils, FrameControl} from '../zcl';
import Touchlink from './touchlink';
import GreenPower from './greenPower';

// @ts-ignore
import mixin from 'mixin-deep';
Expand Down Expand Up @@ -60,6 +61,7 @@ class Controller extends events.EventEmitter {
private options: Options;
private database: Database;
private adapter: Adapter;
private greenPower: GreenPower;
// eslint-disable-next-line
private permitJoinTimer: any;
// eslint-disable-next-line
Expand Down Expand Up @@ -102,6 +104,8 @@ class Controller extends events.EventEmitter {
Entity.injectAdapter(this.adapter);
Entity.injectDatabase(this.database);

this.greenPower = new GreenPower(this.adapter);

// Register adapter events
this.adapter.on(AdapterEvents.Events.deviceJoined, this.onDeviceJoined.bind(this));
this.adapter.on(AdapterEvents.Events.zclData, (data) => this.onZclOrRawData('zcl', data));
Expand Down Expand Up @@ -158,11 +162,13 @@ class Controller extends events.EventEmitter {
if (permit && !this.getPermitJoin()) {
debug.log('Permit joining');
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
await this.greenPower.enableCommisioning(254);

// Zigbee 3 networks automatically close after max 255 seconds, keep network open.
this.permitJoinTimer = setInterval(async (): Promise<void> => {
debug.log('Permit joining');
await this.adapter.permitJoin(254, !device ? null : device.networkAddress);
await this.greenPower.enableCommisioning(254);
}, 200 * 1000);
} else if (permit && this.getPermitJoin()) {
debug.log('Joining already permitted');
Expand Down Expand Up @@ -415,6 +421,13 @@ class Controller extends events.EventEmitter {

const device = typeof dataPayload.address === 'string' ?
Device.byIeeeAddr(dataPayload.address) : Device.byNetworkAddress(dataPayload.address);

if (this.isZclDataPayload(dataPayload, 'zcl') && dataPayload.frame.Cluster.name === 'greenPower') {
console.log(dataPayload.frame.Payload);

this.greenPower.onZclGreenPowerData(dataPayload);
}

if (!device) {
debug.log(
`'${dataType}' data is from unknown device with address '${dataPayload.address}', ` +
Expand Down
62 changes: 62 additions & 0 deletions src/controller/greenPower.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {TsType as AdapterTsType, Adapter, Events as AdapterEvents} from '../adapter';
import * as Zcl from '../zcl';

class GreenPower {
private adapter: Adapter;

public constructor(adapter: Adapter) {
this.adapter = adapter;
}

public async onZclGreenPowerData(dataPayload: AdapterEvents.ZclDataPayload): Promise<void> {
// if (dataPayload.frame.getCommand().name === 'commisioningNotification') {
// const payload = {
// options: 0x00e548,
// srcID: dataPayload.frame.Payload.srcID,
// sinkGroupID: 0x0b84,
// deviceID: dataPayload.frame.Payload.commandFrame.deviceID,
// frameCounter: 491,
// gpdKey: [0x1d, 0xd5, 0x12, 0x34, 0xd5, 0x34, 0x98, 0x58, 0xb7, 0x31, 0x65, 0x6e, 0xd1, 0xf8, 0xf4, 0x8c],
// };

// const frame = Zcl.ZclFrame.create(
// Zcl.FrameType.SPECIFIC, Zcl.Direction.SERVER_TO_CLIENT, true,
// null, 100, 'pairing', 33, payload
// );

// await this.adapter.sendZclFrameBroadcast(frame);
// }
}

public async enableCommisioning(time: number): Promise<void> {
{
///await this.adapter.znp.request(5, 'extAddGroup', {endpoint: 242, groupid: 0x0b84, namelen: 0, groupname:[]});
// const cluster = Zcl.Utils.getCluster('genGroups');
// const command = cluster.getCommand('add');
// const payload = {groupid: 0x0b84, groupname: ''};
// const frame = Zcl.ZclFrame.create(
// Zcl.FrameType.SPECIFIC, Zcl.Direction.CLIENT_TO_SERVER, false,
// null, 100, command.ID, cluster.ID, payload
// );

// await this.adapter.sendZclFrameNetworkAddressWithResponse(
// 0x00, 242, frame, 10000, 15000,
// );
}


const payload = {
options: 0x0b,
commisioningWindow: time,
};

const frame = Zcl.ZclFrame.create(
Zcl.FrameType.SPECIFIC, Zcl.Direction.SERVER_TO_CLIENT, true,
null, 100, 'commisioningMode', 33, payload
);

await this.adapter.sendZclFrameBroadcast(frame);
}
}

export default GreenPower;
18 changes: 18 additions & 0 deletions src/zcl/buffaloZcl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,22 @@ class BuffaloZcl extends Buffalo {
}
}

private readGdpFrame(options: TsType.Options): TsType.Value {
// Commisioning
if (options.message.commandID === 224) {
return {
deviceID: this.readUInt8(),
options: this.readUInt8(),
extendedOptions: this.readUInt8(),
securityKey: this.readBuffer(16),
keyMic: this.readUInt32(),
outgoingCounter: this.readUInt32(),
};
}

return {};
}

private readUInt40(): TsType.Value {
const lsb = this.readUInt32();
const msb = this.readUInt8();
Expand Down Expand Up @@ -327,6 +343,8 @@ class BuffaloZcl extends Buffalo {
return this.readListZoneInfo(options);
} else if (type === 'LIST_THERMO_TRANSITIONS') {
return this.readListThermoTransitions(options);
} else if (type === 'GDP_FRAME') {
return this.readGdpFrame(options);
} else if (type === 'uint40') {
return this.readUInt40();
} else if (type === 'uint48') {
Expand Down
1 change: 1 addition & 0 deletions src/zcl/definition/buffaloZclDataType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum BuffaloZclDataType {
EXTENSION_FIELD_SETS = 1006,
LIST_THERMO_TRANSITIONS = 1007,
BUFFER = 1008,
GDP_FRAME = 1009,
};

export default BuffaloZclDataType;
49 changes: 49 additions & 0 deletions src/zcl/definition/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,55 @@ const Cluster: {
},
},
},
greenPower: {
ID: 33,
attributes: {
},
commands: {
notification: {
ID: 0,
parameters: [
{name: 'options', type: DataType.uint16},
{name: 'srcID', type: DataType.uint32},
{name: 'frameCounter', type: DataType.uint32},
{name: 'commandID', type: DataType.uint8},
{name: 'payloadSize', type: DataType.uint8},
{name: 'commandFrame', type: BuffaloZclDataType.GDP_FRAME},
],
},
commisioningNotification: {
ID: 4,
parameters: [
{name: 'options', type: DataType.uint16},
{name: 'srcID', type: DataType.uint32},
{name: 'frameCounter', type: DataType.uint32},
{name: 'commandID', type: DataType.uint8},
{name: 'payloadSize', type: DataType.uint8},
{name: 'commandFrame', type: BuffaloZclDataType.GDP_FRAME},
],
},
},
commandsResponse: {
pairing: {
ID: 1,
parameters: [
{name: 'options', type: DataType.uint24},
{name: 'srcID', type: DataType.uint32},
{name: 'sinkGroupID', type: DataType.uint16},
{name: 'deviceID', type: DataType.uint8},
{name: 'frameCounter', type: DataType.uint32},
{name: 'gpdKey', type: BuffaloZclDataType.LIST_UINT8},
],
},
commisioningMode: {
ID: 2,
parameters: [
{name: 'options', type: DataType.uint8},
{name: 'commisioningWindow', type: DataType.uint16},
],
},
},
},
mobileDeviceCfg: {
ID: 34,
attributes: {
Expand Down
5 changes: 5 additions & 0 deletions src/zcl/zclFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ class ZclFrame {
}

const typeStr = ZclFrame.getDataTypeString(parameter.type);

if (parameter.type === BuffaloZclDataType.GDP_FRAME) {
options.message = payload;
}

payload[parameter.name] = buffalo.read(typeStr, options);
}

Expand Down
22 changes: 22 additions & 0 deletions test/zcl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,28 @@ describe('Zcl', () => {
expect(frame.Payload).toStrictEqual(payload);
});

it('onlythis ZclFrame from buffer GDP', () => {
const buffer = [0x11, 0x00, 0x04, 0x00, 0x00, 0xfe, 0xf4, 0x46, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xe0, 0x1b, 0x02, 0x81, 0xf2, 0xf1, 0xec, 0x92, 0xab, 0xff, 0x8f, 0x13, 0x63, 0xe1, 0x46, 0xbe, 0xb5, 0x18, 0xc9, 0x0c, 0xab, 0xa4, 0x46, 0xd4, 0xd5, 0xf9, 0x01, 0x00, 0x00];
const frame = Zcl.ZclFrame.fromBuffer(Zcl.Utils.getCluster("greenPower").ID, Buffer.from(buffer));
const header = {
commandIdentifier: 4,
frameControl: {
direction: 0,
disableDefaultResponse: true,
frameType: 1,
manufacturerSpecific: false,
},
manufacturerCode: null,
transactionSequenceNumber: 0,
};

const payload = [{status: Zcl.Status.SUCCESS, attrId: 1, dataType: Zcl.DataType.data8, attrData: 3}];

expect(frame.Header).toStrictEqual(header);
console.log(frame.Payload);
//expect(frame.Payload).toStrictEqual(payload);
});

it('ZclFrame from buffer readRsp alias type', () => {
const buffer = [8, 1, 1, 1, 0, 0, 8, 3];
const frame = Zcl.ZclFrame.fromBuffer(Zcl.Utils.getCluster("genBasic").ID, Buffer.from(buffer));
Expand Down

0 comments on commit 7b81483

Please sign in to comment.