Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/adapter/ember/adapter/emberAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ type StackConfig = {
TRANSIENT_KEY_TIMEOUT_S: number;
/**@see Ezsp.ezspSetRadioIeee802154CcaMode */
CCA_MODE?: keyof typeof IEEE802154CcaMode;
/** (Default: undefined) @see EzspConfigId.SEND_MULTICASTS_TO_SLEEPY_ADDRESS */
SEND_MULTICASTS_TO_SLEEPY_ADDRESS?: boolean;
};

/**
Expand All @@ -194,6 +196,7 @@ export const DEFAULT_STACK_CONFIG: Readonly<StackConfig> = {
END_DEVICE_POLL_TIMEOUT: 8, // zigpc: 8
TRANSIENT_KEY_TIMEOUT_S: 300, // zigpc: 65535
CCA_MODE: undefined, // not set by default
SEND_MULTICASTS_TO_SLEEPY_ADDRESS: undefined, // not set by default
};
/** Default behavior is to disable app key requests */
const ALLOW_APP_KEY_REQUESTS = false;
Expand Down Expand Up @@ -357,6 +360,11 @@ export class EmberAdapter extends Adapter {
logger.error("[STACK CONFIG] Invalid CCA_MODE, ignoring.", NS);
}

if (config.SEND_MULTICASTS_TO_SLEEPY_ADDRESS != null && typeof config.SEND_MULTICASTS_TO_SLEEPY_ADDRESS !== "boolean") {
config.SEND_MULTICASTS_TO_SLEEPY_ADDRESS = undefined;
logger.error("[STACK CONFIG] Invalid SEND_MULTICASTS_TO_SLEEPY_ADDRESS, ignoring.", NS);
}

logger.info(`Using stack config ${JSON.stringify(config)}.`, NS);
return config;
} catch {
Expand Down Expand Up @@ -700,6 +708,13 @@ export class EmberAdapter extends Adapter {
await this.ezsp.ezspSetRadioIeee802154CcaMode(IEEE802154CcaMode[this.stackConfig.CCA_MODE]);
}

// Whether group commands (multicast messages) will be sent to sleepy end-devices too
// (we don't override what the stack was compiled with, if the stackConfig here does not explicitly have a value)
if (this.stackConfig.SEND_MULTICASTS_TO_SLEEPY_ADDRESS != null) {
// validated in `loadStackConfig`
await this.emberSetEzspConfigValue(EzspConfigId.SEND_MULTICASTS_TO_SLEEPY_ADDRESS, +this.stackConfig.SEND_MULTICASTS_TO_SLEEPY_ADDRESS);
}

// WARNING: From here on EZSP commands that affect memory allocation on the NCP should no longer be called (like resizing tables)

await this.registerFixedEndpoints();
Expand Down
23 changes: 23 additions & 0 deletions test/adapter/ember/emberAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ describe("Ember Adapter Layer", () => {
END_DEVICE_POLL_TIMEOUT: 12,
TRANSIENT_KEY_TIMEOUT_S: 500,
CCA_MODE: "SIGNAL_AND_RSSI",
SEND_MULTICASTS_TO_SLEEPY_ADDRESS: true,
};

writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
Expand Down Expand Up @@ -612,6 +613,7 @@ describe("Ember Adapter Layer", () => {
END_DEVICE_POLL_TIMEOUT: 12,
TRANSIENT_KEY_TIMEOUT_S: 500,
CCA_MODE: "SIGNAL_AND_RSSI",
SEND_MULTICASTS_TO_SLEEPY_ADDRESS: true,
};

writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));
Expand All @@ -625,6 +627,7 @@ describe("Ember Adapter Layer", () => {
expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.MAX_END_DEVICE_CHILDREN, config.MAX_END_DEVICE_CHILDREN);
expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.END_DEVICE_POLL_TIMEOUT, config.END_DEVICE_POLL_TIMEOUT);
expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.TRANSIENT_KEY_TIMEOUT_S, config.TRANSIENT_KEY_TIMEOUT_S);
expect(mockEzspSetConfigurationValue).toHaveBeenCalledWith(EzspConfigId.SEND_MULTICASTS_TO_SLEEPY_ADDRESS, 1);
expect(mockEzspSetConcentrator).toHaveBeenCalledWith(
true,
EMBER_LOW_RAM_CONCENTRATOR,
Expand Down Expand Up @@ -658,6 +661,26 @@ describe("Ember Adapter Layer", () => {
unlinkSync(STACK_CONFIG_PATH);
});

it("Starts with custom stack config invalid SEND_MULTICASTS_TO_SLEEPY_ADDRESS", async () => {
const config = {
SEND_MULTICASTS_TO_SLEEPY_ADDRESS: 42,
};

writeFileSync(STACK_CONFIG_PATH, JSON.stringify(config, undefined, 2));

adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
const result = adapter.start();

await vi.advanceTimersByTimeAsync(5000);
await expect(result).resolves.toStrictEqual("resumed");
expect(mockEzspSetConfigurationValue).not.toHaveBeenCalledWith(EzspConfigId.SEND_MULTICASTS_TO_SLEEPY_ADDRESS, 0);
expect(mockEzspSetConfigurationValue).not.toHaveBeenCalledWith(EzspConfigId.SEND_MULTICASTS_TO_SLEEPY_ADDRESS, 1);
expect(loggerSpies.error).toHaveBeenCalledWith("[STACK CONFIG] Invalid SEND_MULTICASTS_TO_SLEEPY_ADDRESS, ignoring.", "zh:ember");

// cleanup
unlinkSync(STACK_CONFIG_PATH);
});

it("Starts with restored when no network in adapter", async () => {
adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS);
const expectedNetParams: EmberNetworkParameters = {
Expand Down