Implement external converters (#3810)

This commit is contained in:
John Doe 2020-06-29 22:16:16 +08:00 committed by GitHub
parent 4b68a482b7
commit def6bf1d0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 1 deletions

View File

@ -26,6 +26,7 @@ const ExtensionBind = require('./extension/bind');
const ExtensionReport = require('./extension/report');
const ExtensionOnEvent = require('./extension/onEvent');
const ExtensionOTAUpdate = require('./extension/otaUpdate');
const ExtensionExternalConverters = require('./extension/externalConverters');
class Controller {
constructor() {
@ -75,6 +76,9 @@ class Controller {
if (settings.get().advanced.availability_timeout) {
this.extensions.push(new ExtensionAvailability(...args));
}
if (settings.get().external_converters.length) {
this.extensions.push(new ExtensionExternalConverters(...args));
}
const extensionPath = data.joinPath('extension');
if (fs.existsSync(extensionPath)) {

View File

@ -0,0 +1,31 @@
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../util/settings');
const Extension = require('./extension');
const data = require('../util/data');
class ExternalConverters extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
super(zigbee, mqtt, state, publishEntityState, eventBus);
const externalConverters = settings.get().external_converters;
externalConverters.forEach((moduleName) => {
let converterModule = moduleName;
if (moduleName.endsWith('.js')) {
converterModule = data.joinPath(moduleName.split('.')[0]);
}
const converter = require(converterModule);
if (Array.isArray(converter)) {
converter.forEach((mod) => {
zigbeeHerdsmanConverters.addDeviceDefinition(mod);
});
} else {
zigbeeHerdsmanConverters.addDeviceDefinition(converter);
}
});
}
}
module.exports = ExternalConverters;

View File

@ -122,6 +122,7 @@ const defaults = {
*/
timestamp_format: 'YYYY-MM-DD HH:mm:ss',
},
external_converters: [],
};
const schema = {
@ -271,6 +272,10 @@ const schema = {
},
},
},
external_converters: {
type: 'array',
items: {type: 'string'},
},
required: ['homeassistant', 'permit_join', 'mqtt'],
};

View File

@ -0,0 +1,7 @@
const mockDevices = [{
mock: 1
}, {
mock: 2
}];
module.exports = mockDevices;

View File

@ -0,0 +1,5 @@
const mockDevice = {
mock: true
};
module.exports = mockDevice;

View File

@ -0,0 +1,114 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const path = require('path');
const fs = require('fs');
zigbeeHerdsmanConverters.addDeviceDefinition = jest.fn();
const mocksClear = [zigbeeHerdsmanConverters.addDeviceDefinition, zigbeeHerdsman.permitJoin,
mockExit, MQTT.end, zigbeeHerdsman.stop, logger.debug,
MQTT.publish, MQTT.connect, zigbeeHerdsman.devices.bulb_color.removeFromNetwork,
zigbeeHerdsman.devices.bulb.removeFromNetwork, logger.error,
];
jest.mock(
'mock-external-converter-module', () => {
return {
mock: true
};
}, {
virtual: true
});
jest.mock(
'mock-multiple-external-converter-module', () => {
return [{
mock: 1
}, {
mock: 2
}];
}, {
virtual: true
});
describe('Loads external converters', () => {
let controller;
beforeEach(async () => {
jest.useRealTimers();
await flushPromises();
mocksClear.forEach((m) => m.mockClear());
data.writeDefaultConfiguration();
data.writeEmptyState();
settings._reRead();
});
it('Does not load external converters', async () => {
settings.set(['external_converters'], []);
controller = new Controller();
await controller.start();
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(0);
});
it('Loads external converters', async () => {
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter.js'), path.join(data.mockDir, 'mock-external-converter.js'));
const devicesCount = zigbeeHerdsman.devices.lenght;
settings.set(['external_converters'], ['mock-external-converter.js']);
controller = new Controller();
await controller.start();
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
mock: true
});
});
it('Loads multiple external converters', async () => {
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter-multiple.js'), path.join(data.mockDir, 'mock-external-converter-multiple.js'));
const devicesCount = zigbeeHerdsman.devices.lenght;
settings.set(['external_converters'], ['mock-external-converter-multiple.js']);
controller = new Controller();
await controller.start();
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
mock: 1
});
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(2, {
mock: 2
});
});
it('Loads external converters from package', async () => {
settings.set(['external_converters'], ['mock-external-converter-module']);
controller = new Controller();
await controller.start();
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
mock: true
});
});
it('Loads multiple external converters from package', async () => {
settings.set(['external_converters'], ['mock-multiple-external-converter-module']);
controller = new Controller();
await controller.start();
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
mock: 1
});
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(2, {
mock: 2
});
});
});

View File

@ -174,7 +174,8 @@ function writeDefaultConfiguration() {
retain: false,
devices: ['bulb_2']
},
}
},
external_converters: [],
};
yaml.writeIfChanged(path.join(mockDir, 'configuration.yaml'), config);