Skip to content

Commit

Permalink
fix: Allow to set adapter transmit power on startup (#1139)
Browse files Browse the repository at this point in the history
* fix: Allow to set adapter transmit power on startup

* Update ember part.

* Fix prettier.

---------

Co-authored-by: Nerivec <62446222+Nerivec@users.noreply.github.com>
  • Loading branch information
Koenkk and Nerivec authored Aug 4, 2024
1 parent 9bded2b commit 036a2d5
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 51 deletions.
13 changes: 7 additions & 6 deletions src/adapter/ember/adapter/emberAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import SocketPortUtils from '../../socketPortUtils';
import {
EMBER_INSTALL_CODE_CRC_SIZE,
EMBER_INSTALL_CODE_SIZES,
EMBER_NUM_802_15_4_CHANNELS,
EMBER_MIN_802_15_4_CHANNEL_NUMBER,
LONG_DEST_FRAME_CONTROL,
MAC_ACK_REQUIRED,
MAXIMUM_INTERPAN_LENGTH,
Expand All @@ -32,7 +30,6 @@ import {
STACK_PROFILE_ZIGBEE_PRO,
SECURITY_LEVEL_Z3,
INVALID_RADIO_CHANNEL,
EMBER_ALL_802_15_4_CHANNELS_MASK,
ZIGBEE_PROFILE_INTEROPERABILITY_LINK_KEY,
EMBER_MIN_BROADCAST_ADDRESS,
} from '../consts';
Expand Down Expand Up @@ -790,6 +787,10 @@ export class EmberAdapter extends Adapter {
throw new Error(`Failed to get network parameters with status=${SLStatus[status]}.`);
}

if (this.adapterOptions.transmitPower != null && parameters.radioTxPower !== this.adapterOptions.transmitPower) {
await this.setTransmitPower(this.adapterOptions.transmitPower);
}

this.networkCache.parameters = parameters;
this.networkCache.eui64 = await this.ezsp.ezspGetEui64();

Expand Down Expand Up @@ -1143,12 +1144,12 @@ export class EmberAdapter extends Adapter {
const netParams: EmberNetworkParameters = {
panId,
extendedPanId,
radioTxPower: 5,
radioTxPower: this.adapterOptions.transmitPower || 5,
radioChannel,
joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
nwkUpdateId: 0,
channels: EMBER_ALL_802_15_4_CHANNELS_MASK,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
};

logger.info(`[INIT FORM] Forming new network with: ${JSON.stringify(netParams)}`, NS);
Expand Down Expand Up @@ -1853,7 +1854,7 @@ export class EmberAdapter extends Adapter {
throw new Error(`[BACKUP] Failed to export Network Key with status=${SLStatus[nkStatus]}.`);
}

const zbChannels = Array.from(Array(EMBER_NUM_802_15_4_CHANNELS), (e, i) => i + EMBER_MIN_802_15_4_CHANNEL_NUMBER);
const zbChannels = Array.from(Array(ZSpec.NUM_802_15_4_CHANNELS), (e, i) => i + ZSpec.MIN_802_15_4_CHANNEL_NUMBER);

return {
networkOptions: {
Expand Down
29 changes: 0 additions & 29 deletions src/adapter/ember/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,6 @@ export const SOURCE_ROUTE_OVERHEAD_UNKNOWN = 0xff;
// 0xFFF8 indicates a memory-constrained many-to-one route request
export const EMBER_MIN_BROADCAST_ADDRESS = 0xfff8;

/** The maximum 802.15.4 channel number is 26. */
export const EMBER_MAX_802_15_4_CHANNEL_NUMBER = 26;
/** The minimum 2.4GHz 802.15.4 channel number is 11. */
export const EMBER_MIN_802_15_4_CHANNEL_NUMBER = 11;
/** The minimum SubGhz channel number is 0. */
export const EMBER_MIN_SUBGHZ_CHANNEL_NUMBER = 0;

/**
* ZigBee protocol specifies that active scans have a duration of 3 (138 msec).
* See documentation for emberStartScan in include/network-formation.h
* for more info on duration values.
*/
export const EMBER_ACTIVE_SCAN_DURATION = 3;
/** The SubGhz scan duration is 5. */
export const EMBER_SUB_GHZ_SCAN_DURATION = 5;
/** There are sixteen 802.15.4 channels. */
export const EMBER_NUM_802_15_4_CHANNELS = EMBER_MAX_802_15_4_CHANNEL_NUMBER - EMBER_MIN_802_15_4_CHANNEL_NUMBER + 1;
/** A bitmask to scan all 2.4 GHz 802.15.4 channels. */
export const EMBER_ALL_802_15_4_CHANNELS_MASK = 0x07fff800;
/** The channels that the plugin will preferentially scan when forming and joining. */
export const NETWORK_FIND_CHANNEL_MASK = 0x0318c800;
/**
* Cut-off value (dBm) <-128..127>
* The maximum noise allowed on a channel to consider for forming a network.
* If the noise on all preferred channels is above this limit and "Enable scanning all channels" is ticked, the scan continues on all channels.
* Use emberAfPluginNetworkFindGetEnergyThresholdForChannelCallback() to override this value.
*/
export const NETWORK_FIND_CUT_OFF_VALUE = -48;

/**
* The additional overhead required for network source routing (relay count = 1, relay index = 1).
* This does not include the size of the relay list itself.
Expand Down
4 changes: 2 additions & 2 deletions src/adapter/ember/utils/initters.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* istanbul ignore file */
import * as ZSpec from '../../../zspec';
import {NetworkCache} from '../adapter/emberAdapter';
import {ZB_PSA_ALG, INVALID_RADIO_CHANNEL, EMBER_ALL_802_15_4_CHANNELS_MASK} from '../consts';
import {ZB_PSA_ALG, INVALID_RADIO_CHANNEL} from '../consts';
import {EmberJoinMethod, SecManDerivedKeyType, SecManFlag, SecManKeyType} from '../enums';
import {EMBER_AES_HASH_BLOCK_SIZE} from '../ezsp/consts';
import {EmberAesMmoHashContext, SecManContext} from '../types';
Expand All @@ -21,7 +21,7 @@ export const initNetworkCache = (): NetworkCache => {
joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
nwkManagerId: ZSpec.NULL_NODE_ID,
nwkUpdateId: 0,
channels: EMBER_ALL_802_15_4_CHANNELS_MASK,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
},
};
};
Expand Down
1 change: 1 addition & 0 deletions src/adapter/tstype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface AdapterOptions {
concurrent?: number;
delay?: number;
disableLED: boolean;
transmitPower?: number;
forceStartWithInconsistentAdapterConfiguration?: boolean;
}

Expand Down
4 changes: 4 additions & 0 deletions src/adapter/z-stack/adapter/zStackAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ class ZStackAdapter extends Adapter {
await this.setLED('disable');
}

if (this.adapterOptions.transmitPower != null) {
await this.setTransmitPower(this.adapterOptions.transmitPower);
}

return startResult;
}

Expand Down
107 changes: 93 additions & 14 deletions test/adapter/ember/emberAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
EmberDeviceUpdate,
EmberIncomingMessageType,
EmberJoinDecision,
EmberJoinMethod,
EmberKeyStructBitmask,
EmberNetworkStatus,
EmberNodeType,
Expand Down Expand Up @@ -622,27 +623,25 @@ describe('Ember Adapter Layer', () => {
it('Starts with restored when network key mismatch but backup available', async () => {
adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);

mockEzspGetNetworkParameters.mockResolvedValueOnce([
SLStatus.OK,
EmberNodeType.COORDINATOR,
{
extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
panId: DEFAULT_NETWORK_OPTIONS.panID,
radioTxPower: 5,
radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
joinMethod: 0,
nwkManagerId: 0,
nwkUpdateId: 0,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
} as EmberNetworkParameters,
]);
const expectedNetParams: EmberNetworkParameters = {
extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
panId: DEFAULT_NETWORK_OPTIONS.panID,
radioTxPower: 5,
radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
joinMethod: 0,
nwkManagerId: 0,
nwkUpdateId: 0,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
};
mockEzspGetNetworkParameters.mockResolvedValueOnce([SLStatus.OK, EmberNodeType.COORDINATOR, expectedNetParams]);
const contents = Buffer.from(DEFAULT_BACKUP.network_key.key, 'hex').fill(0xff);
mockEzspExportKey.mockResolvedValueOnce([SLStatus.OK, {contents} as SecManKey]);

const result = adapter.start();

await jest.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual('restored');
expect(mockEzspFormNetwork).toHaveBeenCalledWith(expectedNetParams);
});

it('Starts with reset when networks mismatch but no backup available', async () => {
Expand Down Expand Up @@ -682,6 +681,86 @@ describe('Ember Adapter Layer', () => {

await jest.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual('reset');
expect(mockEzspFormNetwork).toHaveBeenCalledWith({
panId: 1234,
extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
radioTxPower: 5, // default when setting `transmitPower` is null/zero
radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
nwkUpdateId: 0,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
} as EmberNetworkParameters);
});

it('Starts with reset and forms with given transmit power', async () => {
adapter = new EmberAdapter(
Object.assign({}, DEFAULT_NETWORK_OPTIONS, {panID: 1234}),
DEFAULT_SERIAL_PORT_OPTIONS,
backupPath,
Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
);

const result = adapter.start();

await jest.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual('reset');
expect(mockEzspFormNetwork).toHaveBeenCalledWith({
panId: 1234,
extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
radioTxPower: 10,
radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
joinMethod: EmberJoinMethod.MAC_ASSOCIATION,
nwkManagerId: ZSpec.COORDINATOR_ADDRESS,
nwkUpdateId: 0,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
} as EmberNetworkParameters);
});

it('Starts with mismatching transmit power', async () => {
adapter = new EmberAdapter(
DEFAULT_NETWORK_OPTIONS,
DEFAULT_SERIAL_PORT_OPTIONS,
backupPath,
Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
);

const result = adapter.start();

await jest.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual('resumed');
expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1);
expect(mockEzspSetRadioPower).toHaveBeenCalledWith(10);
});

it('Starts with matching transmit power after form', async () => {
adapter = new EmberAdapter(
DEFAULT_NETWORK_OPTIONS,
DEFAULT_SERIAL_PORT_OPTIONS,
backupPath,
Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 10}),
);
mockEzspNetworkInit.mockResolvedValueOnce(SLStatus.NOT_JOINED);
mockEzspGetNetworkParameters.mockResolvedValueOnce([
SLStatus.OK,
EmberNodeType.COORDINATOR,
{
extendedPanId: DEFAULT_NETWORK_OPTIONS.extendedPanID!,
panId: DEFAULT_NETWORK_OPTIONS.panID,
radioTxPower: 10,
radioChannel: DEFAULT_NETWORK_OPTIONS.channelList[0],
joinMethod: 0,
nwkManagerId: 0,
nwkUpdateId: 0,
channels: ZSpec.ALL_802_15_4_CHANNELS_MASK,
} as EmberNetworkParameters,
]);

const result = adapter.start();

await jest.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual('restored');
expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(0);
});

it('Fails to start when EZSP layer fails to start', async () => {
Expand Down
7 changes: 7 additions & 0 deletions test/adapter/z-stack/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,13 @@ describe('zstack-adapter', () => {
});
});

it('Start with transmit power set', async () => {
basicMocks();
adapter = new ZStackAdapter(networkOptions, serialPortOptions, 'backup.json', {transmitPower: 2, disableLED: false});
await adapter.start();
expect(mockZnpRequest).toBeCalledWith(Subsystem.SYS, 'stackTune', {operation: 0, value: 2});
});

it('Set transmit power', async () => {
basicMocks();
await adapter.start();
Expand Down

0 comments on commit 036a2d5

Please sign in to comment.