diff --git a/.gitignore b/.gitignore index 0802e11f0..f5c70fb90 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ data-backup/ data/coordinator_backup.json data/.storage data/database.db.backup +data/extension # MacOS indexing file .DS_Store \ No newline at end of file diff --git a/lib/controller.js b/lib/controller.js index d6a2fb9a0..fc3a06415 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -6,6 +6,9 @@ const logger = require('./util/logger'); const settings = require('./util/settings'); const objectAssignDeep = require('object-assign-deep'); const utils = require('./util/utils'); +const fs = require('fs'); +const data = require('./util/data'); +const path = require('path'); // Extensions const ExtensionPublish = require('./extension/publish'); @@ -68,6 +71,15 @@ class Controller { if (settings.get().advanced.availability_timeout) { this.extensions.push(new ExtensionAvailability(...args)); } + + const extensionPath = data.joinPath('extension'); + if (fs.existsSync(extensionPath)) { + const extensions = fs.readdirSync(extensionPath); + for (const extension of extensions) { + const Extension = require(path.join(extensionPath, extension.split('.')[0])); + this.extensions.push(new Extension(...args)); + } + } } async start() { diff --git a/test/assets/exampleExtension.js b/test/assets/exampleExtension.js new file mode 100644 index 000000000..60e7ee0cd --- /dev/null +++ b/test/assets/exampleExtension.js @@ -0,0 +1,11 @@ +class Example { + constructor(zigbee, mqtt, state, publishEntityState, eventBus) { + this.mqtt = mqtt; + } + + onMQTTConnected() { + this.mqtt.publish('example/extension', 'test') + } +} + +module.exports = Example; diff --git a/test/controller.test.js b/test/controller.test.js index 82bd7af9b..22d58ada0 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -529,4 +529,15 @@ describe('Controller', () => { await flushPromises(); expect(controller.state.state).toStrictEqual({}); }); + + it('Load user extension', async () => { + const extensionPath = path.join(data.mockDir, 'extension'); + fs.mkdirSync(extensionPath); + fs.copyFileSync(path.join(__dirname, 'assets', 'exampleExtension.js'), path.join(extensionPath, 'exampleExtension.js')) + controller = new Controller(); + await controller.start(); + await flushPromises(); + expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'test', { retain: false, qos: 0 }, expect.any(Function)); + }); + });