diff --git a/src/adapter/adapter.ts b/src/adapter/adapter.ts index c273b592cd..b8817334c5 100644 --- a/src/adapter/adapter.ts +++ b/src/adapter/adapter.ts @@ -97,8 +97,6 @@ abstract class Adapter extends events.EventEmitter { public abstract getNetworkParameters(): Promise; - public abstract setTransmitPower(value: number): Promise; - public abstract addInstallCode(ieeeAddress: string, key: Buffer): Promise; public abstract waitFor( diff --git a/src/adapter/deconz/adapter/deconzAdapter.ts b/src/adapter/deconz/adapter/deconzAdapter.ts index a59a65eb4c..8268c416fe 100644 --- a/src/adapter/deconz/adapter/deconzAdapter.ts +++ b/src/adapter/deconz/adapter/deconzAdapter.ts @@ -574,11 +574,6 @@ class DeconzAdapter extends Adapter { throw new Error('not supported'); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async setTransmitPower(value: number): Promise { - throw new Error('not supported'); - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars public async sendZclFrameInterPANIeeeAddr(zclFrame: Zcl.Frame, ieeeAddr: string): Promise { throw new Error('not supported'); diff --git a/src/adapter/ember/adapter/emberAdapter.ts b/src/adapter/ember/adapter/emberAdapter.ts index be348a20b7..0586e7dc3a 100644 --- a/src/adapter/ember/adapter/emberAdapter.ts +++ b/src/adapter/ember/adapter/emberAdapter.ts @@ -725,8 +725,13 @@ 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); + if (this.adapterOptions.transmitPower != undefined && parameters.radioTxPower !== this.adapterOptions.transmitPower) { + const status = await this.ezsp.ezspSetRadioPower(this.adapterOptions.transmitPower); + + if (status !== SLStatus.OK) { + // soft-fail, don't prevent start + logger.error(`Failed to set transmit power to ${this.adapterOptions.transmitPower} status=${SLStatus[status]}.`, NS); + } } this.networkCache.parameters = parameters; @@ -1713,17 +1718,6 @@ export class EmberAdapter extends Adapter { }); } - // queued - public async setTransmitPower(value: number): Promise { - return await this.queue.execute(async () => { - const status = await this.ezsp.ezspSetRadioPower(value); - - if (status !== SLStatus.OK) { - throw new Error(`Failed to set transmit power to ${value} status=${SLStatus[status]}.`); - } - }); - } - // queued public async addInstallCode(ieeeAddress: string, key: Buffer): Promise { // codes with CRC, check CRC before sending to NCP, otherwise let NCP handle diff --git a/src/adapter/ezsp/adapter/ezspAdapter.ts b/src/adapter/ezsp/adapter/ezspAdapter.ts index 93528ac1da..c938387d4c 100644 --- a/src/adapter/ezsp/adapter/ezspAdapter.ts +++ b/src/adapter/ezsp/adapter/ezspAdapter.ts @@ -142,7 +142,7 @@ class EZSPAdapter extends Adapter { `'ezsp' driver is deprecated and will only remain to provide support for older firmware (pre 7.4.x). Migration to 'ember' is recommended. If using Zigbee2MQTT see https://github.com/Koenkk/zigbee2mqtt/discussions/21462`, NS, ); - return await this.driver.startup(); + return await this.driver.startup(this.adapterOptions.transmitPower); } public async stop(): Promise { @@ -542,13 +542,6 @@ class EZSPAdapter extends Adapter { }); } - public async setTransmitPower(value: number): Promise { - logger.debug(`setTransmitPower to ${value}`, NS); - return await this.queue.execute(async () => { - await this.driver.setRadioPower(value); - }); - } - public async setChannelInterPAN(channel: number): Promise { return await this.queue.execute(async () => { this.interpanLock = true; diff --git a/src/adapter/ezsp/driver/driver.ts b/src/adapter/ezsp/driver/driver.ts index e6fb969dfe..f1bd4899a4 100644 --- a/src/adapter/ezsp/driver/driver.ts +++ b/src/adapter/ezsp/driver/driver.ts @@ -175,7 +175,7 @@ export class Driver extends EventEmitter { } } - public async startup(): Promise { + public async startup(transmitPower?: number): Promise { let result: TsType.StartResult = 'resumed'; this.transactionID = 1; // this.ezsp = undefined; @@ -260,12 +260,12 @@ export class Driver extends EventEmitter { if (restore) { // restore logger.info('Restore network from backup', NS); - await this.formNetwork(true); + await this.formNetwork(true, transmitPower); result = 'restored'; } else { // reset logger.info('Form network', NS); - await this.formNetwork(false); + await this.formNetwork(false, transmitPower); result = 'reset'; } } @@ -301,6 +301,10 @@ export class Driver extends EventEmitter { await this.multicast.subscribe(ZSpec.GP_GROUP_ID, ZSpec.GP_ENDPOINT); // await this.multicast.subscribe(1, 901); + if (transmitPower != undefined && this.networkParams.radioTxPower !== transmitPower) { + await this.ezsp.execCommand('setRadioPower', {power: transmitPower}); + } + return result; } @@ -318,7 +322,7 @@ export class Driver extends EventEmitter { return !valid; } - private async formNetwork(restore: boolean): Promise { + private async formNetwork(restore: boolean, transmitPower?: number): Promise { let backup; await this.ezsp.execCommand('clearTransientLinkKeys'); @@ -341,7 +345,7 @@ export class Driver extends EventEmitter { await this.ezsp.setInitialSecurityState(initial_security_state); const parameters: EmberNetworkParameters = new EmberNetworkParameters(); - parameters.radioTxPower = 5; + parameters.radioTxPower = transmitPower ?? 5; parameters.joinMethod = EmberJoinMethod.USE_MAC_ASSOCIATION; parameters.nwkManagerId = 0; parameters.nwkUpdateId = 0; @@ -864,10 +868,6 @@ export class Driver extends EventEmitter { ); } - public setRadioPower(value: number): Promise { - return this.ezsp.execCommand('setRadioPower', {power: value}); - } - public setChannel(channel: number): Promise { return this.ezsp.execCommand('setLogicalAndRadioChannel', {radioChannel: channel}); } diff --git a/src/adapter/z-stack/adapter/zStackAdapter.ts b/src/adapter/z-stack/adapter/zStackAdapter.ts index 6e1e207e9e..72db48f53a 100644 --- a/src/adapter/z-stack/adapter/zStackAdapter.ts +++ b/src/adapter/z-stack/adapter/zStackAdapter.ts @@ -152,7 +152,7 @@ class ZStackAdapter extends Adapter { } if (this.adapterOptions.transmitPower != null) { - await this.setTransmitPower(this.adapterOptions.transmitPower); + await this.znp.request(Subsystem.SYS, 'stackTune', {operation: 0, value: this.adapterOptions.transmitPower}); } return await startResult; @@ -953,12 +953,6 @@ class ZStackAdapter extends Adapter { }); } - public async setTransmitPower(value: number): Promise { - return await this.queue.execute(async () => { - await this.znp.request(Subsystem.SYS, 'stackTune', {operation: 0, value}); - }); - } - private waitForInternal( networkAddress: number | undefined, endpoint: number, diff --git a/src/adapter/zboss/adapter/zbossAdapter.ts b/src/adapter/zboss/adapter/zbossAdapter.ts index cf11aa2684..ae09ceaea1 100644 --- a/src/adapter/zboss/adapter/zbossAdapter.ts +++ b/src/adapter/zboss/adapter/zbossAdapter.ts @@ -107,7 +107,7 @@ export class ZBOSSAdapter extends Adapter { await this.driver.connect(); - return await this.driver.startup(); + return await this.driver.startup(this.adapterOptions.transmitPower); } public async stop(): Promise { @@ -171,14 +171,6 @@ export class ZBOSSAdapter extends Adapter { }); } - public async setTransmitPower(value: number): Promise { - if (this.driver.isInitialized()) { - return await this.queue.execute(async () => { - await this.driver.execCommand(CommandId.SET_TX_POWER, {txPower: value}); - }); - } - } - public async addInstallCode(ieeeAddress: string, key: Buffer): Promise { logger.error(() => `NOT SUPPORTED: sendZclFrameToGroup(${ieeeAddress},${key.toString('hex')}`, NS); throw new Error(`Install code is not supported for 'zboss' yet`); diff --git a/src/adapter/zboss/driver.ts b/src/adapter/zboss/driver.ts index 8044815c5e..727a030ed8 100644 --- a/src/adapter/zboss/driver.ts +++ b/src/adapter/zboss/driver.ts @@ -84,7 +84,7 @@ export class ZBOSSDriver extends EventEmitter { await this.execCommand(CommandId.NCP_RESET, {options}, 10000); } - public async startup(): Promise { + public async startup(transmitPower?: number): Promise { logger.info(`Driver startup`, NS); let result: TsType.StartResult = 'resumed'; @@ -135,6 +135,10 @@ export class ZBOSSDriver extends EventEmitter { //await this.execCommand(CommandId.SET_ED_TIMEOUT, {timeout: 8}); //await this.execCommand(CommandId.SET_MAX_CHILDREN, {children: 100}); + if (transmitPower != undefined) { + await this.execCommand(CommandId.SET_TX_POWER, {txPower: transmitPower}); + } + return result; } diff --git a/src/adapter/zigate/adapter/zigateAdapter.ts b/src/adapter/zigate/adapter/zigateAdapter.ts index 4d0c364fca..dc4a43de32 100644 --- a/src/adapter/zigate/adapter/zigateAdapter.ts +++ b/src/adapter/zigate/adapter/zigateAdapter.ts @@ -91,6 +91,10 @@ class ZiGateAdapter extends Adapter { destinationEndpoint: ZSpec.HA_ENDPOINT, groupAddress: default_bind_group, }); + + if (this.adapterOptions.transmitPower != undefined) { + await this.driver.sendCommand(ZiGateCommandCode.SetTXpower, {value: this.adapterOptions.transmitPower}); + } } catch (error) { throw new Error('failed to connect to zigate adapter ' + (error as Error).message); } @@ -190,14 +194,6 @@ class ZiGateAdapter extends Adapter { throw new Error('This adapter does not support backup'); } - public async setTransmitPower(value: number): Promise { - try { - await this.driver.sendCommand(ZiGateCommandCode.SetTXpower, {value: value}); - } catch (error) { - throw new Error(`Set transmitpower failed ${error}`); - } - } - public async sendZdo( ieeeAddress: string, networkAddress: number, diff --git a/src/controller/controller.ts b/src/controller/controller.ts index 501621e82c..f197f6c1c2 100644 --- a/src/controller/controller.ts +++ b/src/controller/controller.ts @@ -513,13 +513,6 @@ class Controller extends events.EventEmitter { await Wait(12000); } - /** - * Set transmit power of the adapter - */ - public async setTransmitPower(value: number): Promise { - return await this.adapter.setTransmitPower(value); - } - public async identifyUnknownDevice(nwkAddress: number): Promise { if (this.unknownDevices.has(nwkAddress)) { // prevent duplicate triggering diff --git a/test/adapter/ember/emberAdapter.test.ts b/test/adapter/ember/emberAdapter.test.ts index 61b91d327a..53e2424a46 100644 --- a/test/adapter/ember/emberAdapter.test.ts +++ b/test/adapter/ember/emberAdapter.test.ts @@ -871,6 +871,24 @@ describe('Ember Adapter Layer', () => { expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(0); }); + it('Starts with mismatching transmit power, failure does not present start', async () => { + adapter = new EmberAdapter( + DEFAULT_NETWORK_OPTIONS, + DEFAULT_SERIAL_PORT_OPTIONS, + backupPath, + Object.assign({}, DEFAULT_ADAPTER_OPTIONS, {transmitPower: 12}), + ); + mockEzspSetRadioPower.mockResolvedValueOnce(SLStatus.FAIL); + + const result = adapter.start(); + + await jest.advanceTimersByTimeAsync(5000); + await expect(result).resolves.toStrictEqual('resumed'); + expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1); + expect(mockEzspSetRadioPower).toHaveBeenCalledWith(12); + expect(loggerSpies.error).toHaveBeenCalledWith(`Failed to set transmit power to 12 status=FAIL.`, 'zh:ember'); + }); + it('Fails to start when EZSP layer fails to start', async () => { adapter = new EmberAdapter(DEFAULT_NETWORK_OPTIONS, DEFAULT_SERIAL_PORT_OPTIONS, backupPath, DEFAULT_ADAPTER_OPTIONS); @@ -2271,18 +2289,6 @@ describe('Ember Adapter Layer', () => { expect(mockEzspGetNetworkParameters).toHaveBeenCalledTimes(1); }); - it('Adapter impl: setTransmitPower', async () => { - await expect(adapter.setTransmitPower(10)).resolves.toStrictEqual(undefined); - expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1); - }); - - it('Adapter impl: throws when setTransmitPower fails', async () => { - mockEzspSetRadioPower.mockResolvedValueOnce(SLStatus.FAIL); - - await expect(adapter.setTransmitPower(10)).rejects.toThrow(`Failed to set transmit power to 10 status=FAIL.`); - expect(mockEzspSetRadioPower).toHaveBeenCalledTimes(1); - }); - it('Adapter impl: addInstallCode without local CRC validation', async () => { await expect(adapter.addInstallCode('0x1122334455667788', Buffer.alloc(16))).resolves.toStrictEqual(undefined); expect(mockEzspAesMmoHash).toHaveBeenCalledTimes(1); diff --git a/test/adapter/z-stack/adapter.test.ts b/test/adapter/z-stack/adapter.test.ts index d5886a32d3..b8afb69bff 100644 --- a/test/adapter/z-stack/adapter.test.ts +++ b/test/adapter/z-stack/adapter.test.ts @@ -2182,16 +2182,6 @@ describe('zstack-adapter', () => { expect(mockZnpRequest).toHaveBeenCalledWith(Subsystem.SYS, 'stackTune', {operation: 0, value: 2}); }); - it('Set transmit power', async () => { - basicMocks(); - await adapter.start(); - mockZnpRequest.mockClear(); - mockQueueExecute.mockClear(); - await adapter.setTransmitPower(15); - expect(mockZnpRequest).toHaveBeenCalledTimes(1); - expect(mockZnpRequest).toHaveBeenCalledWith(Subsystem.SYS, 'stackTune', {operation: 0, value: 15}); - }); - it('Support LED should go to false when LED request fails', async () => { basicMocks(); await adapter.start(); diff --git a/test/controller.test.ts b/test/controller.test.ts index 5444f0c9bb..6cb9e6a876 100755 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -64,7 +64,6 @@ const mockAdapterSupportsBackup = jest.fn().mockReturnValue(true); const mockAdapterReset = jest.fn(); const mockAdapterStop = jest.fn(); const mockAdapterStart = jest.fn().mockReturnValue('resumed'); -const mockAdapterSetTransmitPower = jest.fn(); const mockAdapterGetCoordinatorIEEE = jest.fn().mockReturnValue('0x0000012300000000'); const mockAdapterGetNetworkParameters = jest.fn().mockReturnValue({panID: 1, extendedPanID: 3, channel: 15}); const mocksendZclFrameToGroup = jest.fn(); @@ -399,7 +398,6 @@ jest.mock('../src/adapter/z-stack/adapter/zStackAdapter', () => { }, getNetworkParameters: mockAdapterGetNetworkParameters, waitFor: mockAdapterWaitFor, - setTransmitPower: mockAdapterSetTransmitPower, sendZclFrameToEndpoint: mocksendZclFrameToEndpoint, sendZclFrameToGroup: mocksendZclFrameToGroup, sendZclFrameToAll: mocksendZclFrameToAll, @@ -1615,12 +1613,6 @@ describe('Controller', () => { expect(changeChannelSpy).toHaveBeenCalledTimes(0); }); - it('Set transmit power', async () => { - await controller.start(); - await controller.setTransmitPower(15); - expect(mockAdapterSetTransmitPower).toHaveBeenCalledWith(15); - }); - it('Get coordinator version', async () => { await controller.start(); expect(await controller.getCoordinatorVersion()).toEqual({type: 'zStack', meta: {version: 1}});