Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerivec committed Nov 19, 2024
1 parent 57aac29 commit 0d5a8fd
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 43 deletions.
38 changes: 21 additions & 17 deletions lib/extension/externalConverters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {addDefinition, removeDefinition} from 'zigbee-herdsman-converters';
import {addDefinition, removeExternalDefinitions} from 'zigbee-herdsman-converters';

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / ci

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 22)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

Check failure on line 1 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 22)

Module '"zigbee-herdsman-converters"' has no exported member 'removeExternalDefinitions'.

import logger from '../util/logger';
import ExternalJSExtension from './externalJS';
Expand Down Expand Up @@ -30,29 +30,33 @@ export default class ExternalConverters extends ExternalJSExtension<ModuleExport
);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async removeJS(name: string, module: ModuleExports): Promise<void> {
for (const definition of this.getDefinitions(module)) {
// TODO: implement in ZHC
removeDefinition(definition);
}
removeExternalDefinitions(name);

await this.zigbee.resolveDevicesDefinitions(true);
}

protected async loadJS(name: string, module: ModuleExports): Promise<void> {
for (const definition of this.getDefinitions(module)) {
try {
// TODO: `updateDefinition` in ZHC instead? (add if not exist, replace if exist)
removeDefinition(definition);
try {
removeExternalDefinitions(name);

for (const definition of this.getDefinitions(module)) {
definition.externalConverterName = name;

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / ci

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 18)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 20)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (ubuntu-latest, 22)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 18)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 20)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (macos-latest, 22)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 18)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 20)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

Check failure on line 45 in lib/extension/externalConverters.ts

View workflow job for this annotation

GitHub Actions / tests (windows-latest, 22)

Property 'externalConverterName' does not exist on type 'ExternalDefinition'.

addDefinition(definition);
logger.info(`Loaded external converter '${name}'.`);
} catch (error) {
logger.error(`Failed to load external converter '${name}'`);
logger.error(`Check the code for syntax error and make sure it is up to date with the current Zigbee2MQTT version.`);
logger.error(
`External converters are not meant for long term usage, but for local testing after which a pull request should be created to add out-of-the-box support for the device`,
);

throw error;
}

await this.zigbee.resolveDevicesDefinitions(true);
} catch (error) {
logger.error(`Failed to load external converter '${name}'`);
logger.error(`Check the code for syntax error and make sure it is up to date with the current Zigbee2MQTT version.`);
logger.error(
`External converters are not meant for long term usage, but for local testing after which a pull request should be created to add out-of-the-box support for the device`,
);

throw error;
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/model/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class Device {
}
}

async resolveDefinition(ignoreCache = false): Promise<void> {
async resolveDefinition(ignoreCache: boolean = false): Promise<void> {
if (!this.zh.interviewing && (!this.definition || this._definitionModelID !== this.zh.modelID || ignoreCache)) {
this.definition = await zhc.findByDevice(this.zh, true);
this._definitionModelID = this.zh.modelID;
Expand Down
10 changes: 7 additions & 3 deletions lib/zigbee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ export default class Zigbee {
throw error;
}

for (const device of this.devicesIterator(utils.deviceNotCoordinator)) {
await device.resolveDefinition();
}
await this.resolveDevicesDefinitions();

this.herdsman.on('adapterDisconnected', () => this.eventBus.emitAdapterDisconnected());
this.herdsman.on('lastSeenChanged', (data: ZHEvents.LastSeenChangedPayload) => {
Expand Down Expand Up @@ -239,6 +237,12 @@ export default class Zigbee {
await this.herdsman.permitJoin(time, device?.zh);
}

async resolveDevicesDefinitions(ignoreCache: boolean = false): Promise<void> {
for (const device of this.devicesIterator(utils.deviceNotCoordinator)) {
await device.resolveDefinition(ignoreCache);
}
}

@bind private resolveDevice(ieeeAddr: string): Device | undefined {
if (!this.deviceLookup[ieeeAddr]) {
const device = this.herdsman.getDeviceByIeeeAddr(ieeeAddr);
Expand Down
40 changes: 18 additions & 22 deletions test/extensions/externalConverters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ import * as settings from '../../lib/util/settings';

const BASE_DIR = 'external_converters';

// @ts-expect-error TODO: remove, tmp until implemented
zhc.removeDefinition = jest.fn();

describe('Extension: ExternalConverters', () => {
const mockBasePath = path.join(data.mockDir, BASE_DIR);
let controller: Controller;
Expand All @@ -32,7 +29,7 @@ describe('Extension: ExternalConverters', () => {
const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync');

const zhcAddDefinitionSpy = jest.spyOn(zhc, 'addDefinition');
const zhcRemoveDefinitionSpy = jest.spyOn(zhc, 'removeDefinition');
const zhcRemoveExternalDefinitionsSpy = jest.spyOn(zhc, 'removeExternalDefinitions');

const mocksClear = [
mockMQTT.end,
Expand All @@ -47,7 +44,7 @@ describe('Extension: ExternalConverters', () => {
rmSyncSpy,
writeFileSyncSpy,
zhcAddDefinitionSpy,
zhcRemoveDefinitionSpy,
zhcRemoveExternalDefinitionsSpy,
];

const useAssets = (): void => {
Expand All @@ -72,6 +69,7 @@ describe('Extension: ExternalConverters', () => {
});

beforeEach(async () => {
zhc.removeExternalDefinitions(); // remove all external converters
mocksClear.forEach((m) => m.mockClear());
data.writeDefaultConfiguration();
data.writeDefaultState();
Expand All @@ -81,8 +79,10 @@ describe('Extension: ExternalConverters', () => {
controller = new Controller(jest.fn(), jest.fn());
});

afterEach(() => {
afterEach(async () => {
fs.rmSync(mockBasePath, {recursive: true, force: true});

await controller?.stop();
});

it('loads nothing from folder', async () => {
Expand All @@ -100,7 +100,7 @@ describe('Extension: ExternalConverters', () => {
await controller.start();
await flushPromises();

expect(getZ2MDevice(devices.external_converter_device)!.definition).toMatchObject({
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
description: 'external',
model: 'external_converter_device',
vendor: 'external',
Expand All @@ -115,7 +115,9 @@ describe('Extension: ExternalConverters', () => {
{retain: true, qos: 0},
expect.any(Function),
);
expect(zhcRemoveDefinitionSpy).toHaveBeenCalledTimes(3);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, 'mock-external-converter-multiple.js');
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, 'mock-external-converter.js');
expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
Expand Down Expand Up @@ -157,7 +159,7 @@ describe('Extension: ExternalConverters', () => {
await flushPromises();
mocksClear.forEach((m) => m.mockClear());

expect(getZ2MDevice(devices.external_converter_device)!.definition).toMatchObject({
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
description: 'Automatically generated definition',
model: 'external_converter_device',
vendor: '',
Expand All @@ -168,15 +170,16 @@ describe('Extension: ExternalConverters', () => {
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode}));
await flushPromises();

expect(getZ2MDevice(devices.external_converter_device)!.definition).toMatchObject({
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
description: 'external',
model: 'external_converter_device',
vendor: 'external',
zigbeeModel: ['external_converter_device'],
});
expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true});
expect(writeFileSyncSpy).toHaveBeenCalledWith(converterFilePath, converterCode, 'utf8');
expect(zhcRemoveDefinitionSpy).toHaveBeenCalledTimes(1);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(1);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, converterName);
expect(zhcAddDefinitionSpy).toHaveBeenCalledWith(
expect.objectContaining({
mock: true,
Expand All @@ -197,22 +200,15 @@ describe('Extension: ExternalConverters', () => {
mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName}));
await flushPromises();

expect(getZ2MDevice(devices.external_converter_device)!.definition).toMatchObject({
expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({
description: 'Automatically generated definition',
model: 'external_converter_device',
vendor: '',
zigbeeModel: ['external_converter_device'],
});
expect(rmSyncSpy).toHaveBeenCalledWith(converterFilePath, {force: true});
expect(zhcRemoveDefinitionSpy).toHaveBeenCalledWith(
expect.objectContaining({
mock: true,
zigbeeModel: ['external_converter_device'],
vendor: 'external',
model: 'external_converter_device',
description: 'external',
}),
);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2);
expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, converterName);
expect(mockMQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0}, expect.any(Function));
});

Expand Down Expand Up @@ -299,7 +295,7 @@ describe('Extension: ExternalConverters', () => {

const errorMsg = `Failed to remove definition`;

zhcRemoveDefinitionSpy.mockImplementationOnce(() => {
zhcRemoveExternalDefinitionsSpy.mockImplementationOnce(() => {
throw new Error(errorMsg);
});

Expand Down

0 comments on commit 0d5a8fd

Please sign in to comment.