Support Home Assistant configuration for external converters. https://github.com/Koenkk/zigbee-herdsman-converters/issues/1343

This commit is contained in:
Koen Kanters
2020-07-03 21:20:22 +02:00
parent 1796ba191c
commit 4900a2501d
6 changed files with 79 additions and 23 deletions
+6 -19
View File
@@ -1,30 +1,17 @@
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../util/settings');
const Extension = require('./extension');
const data = require('../util/data');
const utils = require('../util/utils');
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);
}
});
for (const definition of utils.getExternalConvertersDefinitions(settings)) {
const toAdd = {...definition};
delete toAdd['homeassistant'];
zigbeeHerdsmanConverters.addDeviceDefinition(toAdd);
}
}
}
+7
View File
@@ -1,6 +1,7 @@
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../util/settings');
const logger = require('../util/logger');
const utils = require('../util/utils');
const zigbee2mqttVersion = require('../../package.json').version;
const Extension = require('./extension');
const objectAssignDeep = require(`object-assign-deep`);
@@ -1776,6 +1777,12 @@ class HomeAssistant extends Extension {
this.eventBus.on('deviceRemoved', (data) => this.onDeviceRemoved(data.device));
this.eventBus.on('publishEntityState', (data) => this.onPublishEntityState(data));
this.eventBus.on('deviceRenamed', (data) => this.onDeviceRenamed(data.device));
for (const definition of utils.getExternalConvertersDefinitions(settings)) {
if (definition.hasOwnProperty('homeassistant')) {
mapping[definition.model] = definition.homeassistant;
}
}
}
onDeviceRemoved(device) {
+23
View File
@@ -1,6 +1,7 @@
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const equals = require('fast-deep-equal');
const humanizeDuration = require('humanize-duration');
const data = require('./data');
// Xiaomi uses 4151 and 4447 (lumi.plug) as manufacturer ID.
const xiaomiManufacturerID = [4151, 4447];
@@ -150,6 +151,27 @@ function parseJSON(value, failedReturnValue) {
}
}
function* getExternalConvertersDefinitions(settings) {
const externalConverters = settings.get().external_converters;
for (const moduleName of externalConverters) {
let converterModule = moduleName;
if (moduleName.endsWith('.js')) {
converterModule = data.joinPath(moduleName.split('.')[0]);
}
const converter = require(converterModule);
if (Array.isArray(converter)) {
for (const item of converter) {
yield item;
}
} else {
yield converter;
}
}
}
module.exports = {
millisecondsToSeconds: (milliseconds) => milliseconds / 1000,
secondsToMilliseconds: (seconds) => seconds * 1000,
@@ -169,4 +191,5 @@ module.exports = {
getResponse,
capitalize,
parseJSON,
getExternalConvertersDefinitions,
};
@@ -1,5 +1,18 @@
const homeassistantSwitch = {
type: 'switch',
object_id: 'switch',
discovery_payload: {
payload_off: 'OFF',
payload_on: 'ON',
value_template: '{{ value_json.state }}',
command_topic: true,
},
};
const mockDevices = [{
mock: 1
mock: 1,
model: 'external_converters_device',
homeassistant: [homeassistantSwitch],
}, {
mock: 2
}];
+2 -1
View File
@@ -80,7 +80,8 @@ describe('Loads external converters', () => {
await flushPromises();
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
mock: 1
mock: 1,
model: 'external_converters_device',
});
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(2, {
mock: 2
+27 -2
View File
@@ -5,6 +5,9 @@ const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const flushPromises = () => new Promise(setImmediate);
const MQTT = require('./stub/mqtt');
const Controller = require('../lib/controller');
const fs = require('fs');
const path = require('path');
const HomeAssistant = require('../lib/extension/homeassistant');
describe('HomeAssistant extension', () => {
beforeEach(async () => {
@@ -20,7 +23,6 @@ describe('HomeAssistant extension', () => {
it('Should have mapping for all devices supported by zigbee-herdsman-converters', () => {
const missing = [];
const HomeAssistant = require('../lib/extension/homeassistant');
const ha = new HomeAssistant(null, null, null, null, {on: () => {}});
require('zigbee-herdsman-converters').devices.forEach((d) => {
@@ -34,7 +36,6 @@ describe('HomeAssistant extension', () => {
it('Should not have duplicate type/object_ids in a mapping', () => {
const duplicated = [];
const HomeAssistant = require('../lib/extension/homeassistant');
const ha = new HomeAssistant(null, null, null, null, {on: () => {}});
require('zigbee-herdsman-converters').devices.forEach((d) => {
@@ -960,4 +961,28 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish.mock.calls[2][0]).toStrictEqual('homeassistant/device_automation/0x0017880104e45520/click_single/config');
expect(MQTT.publish.mock.calls[3][0]).toStrictEqual('zigbee2mqtt/button/click');
});
it('Load Home Assistant mapping from external converters', async () => {
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter-multiple.js'), path.join(data.mockDir, 'mock-external-converter-multiple.js'));
const beforeCount = Object.entries((new HomeAssistant(null, null, null, null, {on: () => {}}))._getMapping()).length;
settings.set(['external_converters'], ['mock-external-converter-multiple.js']);
controller = new Controller();
const ha = controller.extensions.find((e) => e.constructor.name === 'HomeAssistant');
await controller.start();
await flushPromises();
const afterCount = Object.entries(ha._getMapping()).length;
expect(beforeCount + 1).toStrictEqual(afterCount);
const homeassistantSwitch = {
type: 'switch',
object_id: 'switch',
discovery_payload: {
payload_off: 'OFF',
payload_on: 'ON',
value_template: '{{ value_json.state }}',
command_topic: true,
},
};
expect(ha._getMapping()['external_converters_device']).toStrictEqual([homeassistantSwitch]);
});
});