Skip to content

Commit

Permalink
feat: Support channel change for z-stack (#1110)
Browse files Browse the repository at this point in the history
* feat: Support channel change for z-stack

* updates

* Process feedback and fix tests

* Process feedback
  • Loading branch information
Koenkk authored Jul 16, 2024
1 parent 802dfc1 commit 9c9b58a
Show file tree
Hide file tree
Showing 10 changed files with 27 additions and 51 deletions.
2 changes: 0 additions & 2 deletions src/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,6 @@ abstract class Adapter extends events.EventEmitter {

public abstract getNetworkParameters(): Promise<TsType.NetworkParameters>;

public abstract supportsChangeChannel(): Promise<boolean>;

public abstract changeChannel(newChannel: number): Promise<void>;

public abstract setTransmitPower(value: number): Promise<void>;
Expand Down
6 changes: 1 addition & 5 deletions src/adapter/deconz/adapter/deconzAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1174,12 +1174,8 @@ class DeconzAdapter extends Adapter {
throw new Error('not supported');
}

public async supportsChangeChannel(): Promise<boolean> {
return false;
}

public async changeChannel(newChannel: number): Promise<void> {
throw new Error('not supported');
throw new Error(`Channel change is not supported for 'deconz'`);
}

public async setTransmitPower(value: number): Promise<void> {
Expand Down
4 changes: 0 additions & 4 deletions src/adapter/ember/adapter/emberAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2039,10 +2039,6 @@ export class EmberAdapter extends Adapter {
});
}

public async supportsChangeChannel(): Promise<boolean> {
return true;
}

// queued
public async changeChannel(newChannel: number): Promise<void> {
return this.queue.execute<void>(async () => {
Expand Down
6 changes: 1 addition & 5 deletions src/adapter/ezsp/adapter/ezspAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -765,13 +765,9 @@ class EZSPAdapter extends Adapter {
});
}

public async supportsChangeChannel(): Promise<boolean> {
return false;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async changeChannel(newChannel: number): Promise<void> {
return Promise.reject(new Error('Not supported'));
throw new Error(`Channel change is not supported for 'ezsp'`);
}

public async setTransmitPower(value: number): Promise<void> {
Expand Down
3 changes: 2 additions & 1 deletion src/adapter/z-stack/adapter/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ export class ZnpAdapterManager {
/* istanbul ignore next */
const configMatchesAdapter =
nib &&
Utils.compareChannelLists(this.nwkOptions.channelList, nib.channelList) && // TODO: remove?
// Don't check for channel anymore because channel change is supported.
// Utils.compareChannelLists(this.nwkOptions.channelList, nib.channelList) &&
this.nwkOptions.panId === nib.nwkPanId &&
(this.nwkOptions.extendedPanId.equals(nib.extendedPANID) ||
/* exception for migration from previous code-base */
Expand Down
8 changes: 2 additions & 6 deletions src/adapter/z-stack/adapter/zStackAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1070,10 +1070,6 @@ class ZStackAdapter extends Adapter {
});
}

public async supportsChangeChannel(): Promise<boolean> {
return false;
}

public async changeChannel(newChannel: number): Promise<void> {
return this.queue.execute<void>(async () => {
this.checkInterpanLock();
Expand All @@ -1083,8 +1079,8 @@ class ZStackAdapter extends Adapter {
dstaddrmode: AddressMode.ADDR_BROADCAST,
channelmask: [newChannel].reduce((a, c) => a + (1 << c), 0),
scanduration: 0xfe, // change channel
// scancount: null,// TODO: what's "not present" here?
// nwkmanageraddr: null,// TODO: what's "not present" here?
scancount: 0,
nwkmanageraddr: 0,
};

await this.znp.request(Subsystem.ZDO, 'mgmtNwkUpdateReq', payload);
Expand Down
6 changes: 1 addition & 5 deletions src/adapter/zigate/adapter/zigateAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,9 @@ class ZiGateAdapter extends Adapter {
throw new Error('This adapter does not support backup');
}

public async supportsChangeChannel(): Promise<boolean> {
return false;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async changeChannel(newChannel: number): Promise<void> {
throw new Error('not supported');
throw new Error(`Channel change is not supported for 'zigate'`);
}

public async setTransmitPower(value: number): Promise<void> {
Expand Down
16 changes: 10 additions & 6 deletions src/controller/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,14 @@ class Controller extends events.EventEmitter {
// Check if we have to change the channel, only do this when adapter `resumed` because:
// - `getNetworkParameters` might be return wrong info because it needs to propogate after backup restore
// - If result is not `resumed` (`reset` or `restored`), the adapter should comission with the channel from `this.options.network`
if (startResult === 'resumed' && (await this.adapter.supportsChangeChannel())) {
if (startResult === 'resumed') {
const netParams = await this.getNetworkParameters();
const configuredChannel = this.options.network.channelList[0];
const adapterChannel = netParams.channel;

if (this.options.network.channelList[0] !== netParams.channel) {
await this.changeChannel();
if (configuredChannel != adapterChannel) {
logger.info(`Configured channel '${configuredChannel}' does not match adapter channel '${adapterChannel}', changing channel`, NS);
await this.changeChannel(adapterChannel, configuredChannel);
}
}

Expand Down Expand Up @@ -438,9 +441,10 @@ class Controller extends events.EventEmitter {
/**
* Broadcast a network-wide channel change.
*/
private async changeChannel(): Promise<void> {
logger.info(`Broadcasting change channel to '${this.options.network.channelList[0]}'.`, NS);
await this.adapter.changeChannel(this.options.network.channelList[0]);
private async changeChannel(oldChannel: number, newChannel: number): Promise<void> {
logger.warning(`Changing channel from '${oldChannel}' to '${newChannel}'`, NS);
await this.adapter.changeChannel(newChannel);
logger.info(`Channel changed to '${newChannel}'`, NS);

this.networkParametersCached = null; // invalidate cache
}
Expand Down
8 changes: 2 additions & 6 deletions test/adapter/z-stack/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2278,12 +2278,6 @@ describe('zstack-adapter', () => {
expect(mockZnpRequest).toBeCalledWith(Subsystem.SYS, 'resetReq', {type: 0});
});

it('Supports change channel', async () => {
basicMocks();
await adapter.start();
expect(await adapter.supportsChangeChannel()).toBeFalsy();
});

it('Change channel', async () => {
basicMocks();
await adapter.start();
Expand All @@ -2295,6 +2289,8 @@ describe('zstack-adapter', () => {
dstaddrmode: 15,
channelmask: 0x2000000,
scanduration: 0xfe,
scancount: 0,
nwkmanageraddr: 0,
});
});

Expand Down
19 changes: 8 additions & 11 deletions test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const mockAdapterReset = jest.fn();
const mockAdapterStop = jest.fn();
const mockAdapterStart = jest.fn().mockReturnValue('resumed');
const mockAdapterSetTransmitPower = jest.fn();
const mockAdapterSupportsChangeChannel = jest.fn().mockReturnValue(false);
const mockAdapterChangeChannel = jest.fn();
const mockAdapterGetCoordinator = jest.fn().mockReturnValue({
ieeeAddr: '0x0000012300000000',
Expand Down Expand Up @@ -530,7 +529,6 @@ jest.mock('../src/adapter/z-stack/adapter/zStackAdapter', () => {
getNetworkParameters: mockAdapterGetNetworkParameters,
waitFor: mockAdapterWaitFor,
setTransmitPower: mockAdapterSetTransmitPower,
supportsChangeChannel: mockAdapterSupportsChangeChannel,
changeChannel: mockAdapterChangeChannel,
nodeDescriptor: mockAdapterNodeDescriptor,
activeEndpoints: (networkAddress) => {
Expand Down Expand Up @@ -1726,16 +1724,14 @@ describe('Controller', () => {

it('Change channel', async () => {
await controller.start();
controller.options.network.channelList[0] = 20;
await controller.changeChannel();
await controller.changeChannel(10, 20);
expect(mockAdapterChangeChannel).toHaveBeenCalledWith(20);
mockAdapterGetNetworkParameters.mockReturnValueOnce({panID: 1, extendedPanID: 3, channel: 20});
expect(await controller.getNetworkParameters()).toEqual({panID: 1, channel: 20, extendedPanID: 3});
});

it('Change channel on start if supported', async () => {
it('Change channel on start', async () => {
mockAdapterStart.mockReturnValueOnce('resumed');
mockAdapterSupportsChangeChannel.mockReturnValueOnce(true);
mockAdapterGetNetworkParameters.mockReturnValueOnce({panID: 1, extendedPanID: 3, channel: 25});
await controller.start();
expect(mockAdapterGetNetworkParameters).toHaveBeenCalledTimes(1);
Expand All @@ -1745,19 +1741,20 @@ describe('Controller', () => {

it('Does not change channel on start if not changed', async () => {
mockAdapterStart.mockReturnValueOnce('resumed');
mockAdapterSupportsChangeChannel.mockReturnValueOnce(true);
await controller.start();
expect(mockAdapterGetNetworkParameters).toHaveBeenCalledTimes(1);
expect(mockAdapterChangeChannel).toHaveBeenCalledTimes(0);
});

it('Does not change channel on start if not supported', async () => {
mockAdapterStart.mockReturnValueOnce('resumed');
mockAdapterSupportsChangeChannel.mockReturnValueOnce(false);
mockAdapterChangeChannel.mockImplementationOnce(() => {
throw new Error('Not supported');
});
mockAdapterGetNetworkParameters.mockReturnValueOnce({panID: 1, extendedPanID: 3, channel: 25});
await controller.start();
expect(mockAdapterGetNetworkParameters).toHaveBeenCalledTimes(0);
expect(mockAdapterChangeChannel).toHaveBeenCalledTimes(0);
await expect(controller.start()).rejects.toThrow(`Not supported`);
expect(mockAdapterGetNetworkParameters).toHaveBeenCalledTimes(1);
expect(mockAdapterChangeChannel).toHaveBeenCalledTimes(1);
// get rid of the mockReturnValueOnce that was never called
mockAdapterGetNetworkParameters();
});
Expand Down

0 comments on commit 9c9b58a

Please sign in to comment.