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 Nov 15, 2019
1 parent 7ecf874 commit 6ede3d5
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ abstract class Adapter extends events.EventEmitter {

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 @@ -49,6 +49,7 @@ const Endpoints = [
},
// TERNCY: https://github.com/Koenkk/zigbee-herdsman/issues/82
{...EndpointDefaults, endpoint: 0x6E, appprofid: 0x0104},

This comment has been minimized.

Copy link
@sjorge

sjorge Nov 15, 2019

Contributor

Is there a reason this is noted in hex? I believe this is 110?

{...EndpointDefaults, endpoint: 242, appprofid: 0xa1e0},
];

async function validateItem(
Expand Down
17 changes: 17 additions & 0 deletions src/adapter/z-stack/adapter/zStackAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,22 @@ 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, 242, zclFrame.Cluster.ID,
Constants.AF.DEFAULT_RADIUS, zclFrame.toBuffer(), 10000, 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 Expand Up @@ -506,6 +522,7 @@ class ZStackAdapter extends Adapter {
this.waitress.resolve(payload);
this.emit(Events.Events.zclData, payload);
} catch (error) {
console.log(error);
const payload: Events.RawDataPayload = {
clusterID: object.payload.clusterid,
data: object.payload.data,
Expand Down
12 changes: 12 additions & 0 deletions src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {KeyValue, DeviceType} from './tstype';
import Debug from "debug";
import fs from 'fs';
import {Utils as ZclUtils} from '../zcl';
import GreenPower from './greenPower';

// @ts-ignore
import mixin from 'mixin-deep';
Expand Down Expand Up @@ -63,6 +64,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 @@ -100,6 +102,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 @@ -142,11 +146,13 @@ class Controller extends events.EventEmitter {
if (permit && !this.getPermitJoin()) {
debug.log('Permit joining');
await this.adapter.permitJoin(254);
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);
await this.greenPower.enableCommisioning(254);
}, 200 * 1000);
} else if (permit && this.getPermitJoin()) {
debug.log('Joining already permitted');
Expand Down Expand Up @@ -374,6 +380,12 @@ class Controller extends events.EventEmitter {
): Promise<void> {
debug.log(`Received '${dataType}' data '${JSON.stringify(dataPayload)}'`);

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

this.greenPower.onZclGreenPowerData(dataPayload);
}

const device = Device.byNetworkAddress(dataPayload.networkAddress);
if (!device) {
debug.log(
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 @@ -175,6 +175,22 @@ class BuffaloZcl extends Buffalo {
return value;
}

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 @@ -259,6 +275,8 @@ class BuffaloZcl extends Buffalo {
return this.readExtensionFielSets();
} else if (type === 'LIST_ZONEINFO') {
return this.readListZoneInfo(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 @@ -6,6 +6,7 @@ enum BuffaloZclDataType {
LIST_UINT32 = 1004,
LIST_ZONEINFO = 1005,
EXTENSION_FIELD_SETS = 1006,
GDP_FRAME = 1007,
};

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 @@ -1073,6 +1073,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 @@ -226,6 +226,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 @@ -415,6 +415,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 6ede3d5

Please sign in to comment.