diff --git a/drivers/HmIP-MIOB/assets/icon.svg b/drivers/HmIP-MIOB/assets/icon.svg new file mode 100644 index 0000000..ed8b91e --- /dev/null +++ b/drivers/HmIP-MIOB/assets/icon.svg @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + diff --git a/drivers/HmIP-MIOB/assets/images/large.png b/drivers/HmIP-MIOB/assets/images/large.png new file mode 100644 index 0000000..065c478 Binary files /dev/null and b/drivers/HmIP-MIOB/assets/images/large.png differ diff --git a/drivers/HmIP-MIOB/assets/images/small.png b/drivers/HmIP-MIOB/assets/images/small.png new file mode 100644 index 0000000..d842091 Binary files /dev/null and b/drivers/HmIP-MIOB/assets/images/small.png differ diff --git a/drivers/HmIP-MIOB/device.js b/drivers/HmIP-MIOB/device.js new file mode 100644 index 0000000..306e681 --- /dev/null +++ b/drivers/HmIP-MIOB/device.js @@ -0,0 +1,88 @@ +'use strict'; + +const Device = require('../../lib/device.js'); +const capabilityMap = { + "onoff.channel2": { channel: 2, key: "STATE", set: { key: "STATE", channel: 2 } }, + "onoff.channel3": { channel: 3, key: "STATE", set: { key: "STATE", channel: 3 } }, + "onoff.channel4": { channel: 4, key: "STATE", set: { key: "STATE", channel: 4 } }, + "onoff.channel6": { channel: 6, key: "STATE", set: { key: "STATE", channel: 6 } }, + "onoff.channel7": { channel: 7, key: "STATE", set: { key: "STATE", channel: 7 } }, + "onoff.channel8": { channel: 8, key: "STATE", set: { key: "STATE", channel: 8 } }, + "dim": { channel: 11, key: "LEVEL", set: { key: "LEVEL", channel: 11 } }, +}; + +class HomematicDevice extends Device { + + // Define a getter for the channels to avoid duplication + get channels() { + return [ + { id: "onoff.channel2", showKey: "ShowButtonChannel2", titleKey: "TitleButtonChannel2" }, + { id: "onoff.channel3", showKey: "ShowButtonChannel3", titleKey: "TitleButtonChannel3" }, + { id: "onoff.channel4", showKey: "ShowButtonChannel4", titleKey: "TitleButtonChannel4" }, + { id: "onoff.channel6", showKey: "ShowButtonChannel6", titleKey: "TitleButtonChannel6" }, + { id: "onoff.channel7", showKey: "ShowButtonChannel7", titleKey: "TitleButtonChannel7" }, + { id: "onoff.channel8", showKey: "ShowButtonChannel8", titleKey: "TitleButtonChannel8" }, + ]; + } + + async onInit() { + super.onInit(capabilityMap); + + const settings = this.getSettings(); + + // Configure each channel + for (const channel of this.channels) { + await this.configureChannel(channel, settings); + } + + // Configure dimmer + await this.configureDimmer(settings); + } + + async onSettings({ oldSettings, newSettings, changedKeys }) { + const quickActionChanged = changedKeys.includes("QuickActionChannel"); + + for (const channel of this.channels) { + if ( + quickActionChanged || // Reconfigure all channels if the quick action was changed + changedKeys.includes(channel.showKey) || + changedKeys.includes(channel.titleKey) + ) { + await this.configureChannel(channel, newSettings); + } + } + + // Handle dimmer settings change + if (changedKeys.includes("ShowDimmer") || changedKeys.includes("TitleDimmer")) { + await this.configureDimmer(newSettings); + } + } + + async configureChannel(channel, settings) { + const { id, showKey, titleKey } = channel; + const isQuickAction = settings.QuickActionChannel === id; + + if (!settings[showKey]) { + await this.setCapabilityOptions(id, { uiComponent: null, uiQuickAction: isQuickAction }); + } else { + await this.setCapabilityOptions(id, { + uiComponent: "toggle", + title: settings[titleKey], + uiQuickAction: isQuickAction, + }); + } + } + + async configureDimmer(settings) { + if (!settings.ShowDimmer) { + await this.setCapabilityOptions("dim", { uiComponent: null }); + } else { + await this.setCapabilityOptions("dim", { + uiComponent: "slider", + title: settings.TitleDimmer, + }); + } + } +} + +module.exports = HomematicDevice; diff --git a/drivers/HmIP-MIOB/driver.compose.json b/drivers/HmIP-MIOB/driver.compose.json new file mode 100644 index 0000000..191aebc --- /dev/null +++ b/drivers/HmIP-MIOB/driver.compose.json @@ -0,0 +1,280 @@ +{ + "id": "HmIP-MIOB", + "name": { + "en": "HmIP-MIOB" + }, + "class": "socket", + "capabilities": [], + "capabilitiesOptions": { + "onoff.channel2": { + "title": "Schalter Kanal 2" + }, + "onoff.channel3": { + "title": "Schalter Kanal 3" + }, + "onoff.channel4": { + "title": "Schalter Kanal 4" + }, + "onoff.channel6": { + "title": "Schalter Kanal 6" + }, + "onoff.channel7": { + "title": "Schalter Kanal 7" + }, + "onoff.channel8": { + "title": "Schalter Kanal 8" + }, + "dim": { + "title": "Regler" + } + }, + "images": { + "large": "/drivers/HmIP-MIOB/assets/images/large.png", + "small": "/drivers/HmIP-MIOB/assets/images/small.png" + }, + "pair": [ + { + "id": "list_bridges", + "template": "list_devices", + "options": { + "singular": true + }, + "navigation": { + "next": "list_devices" + } + }, + { + "id": "list_devices", + "template": "list_devices", + "navigation": { + "next": "add_devices" + } + }, + { + "id": "add_devices", + "template": "add_devices" + } + ], + "settings": [ + { + "type": "group", + "label": { + "en": "Device data" + }, + "children": [ + { + "id": "app", + "type": "label", + "label": { + "en": "App" + }, + "value": "Homematic" + }, + { + "id": "driver", + "type": "label", + "label": { + "en": "Driver" + }, + "value": "", + "hint": { + "en": "In most cases the driver name is the same as the device type, but some drivers support multiple device types. In those cases they might not match." + } + }, + { + "id": "address", + "type": "label", + "label": { + "en": "Device Address" + }, + "value": "" + }, + { + "id": "ccuIP", + "type": "label", + "label": { + "en": "CCU IP" + }, + "value": "" + }, + { + "id": "ccuSerial", + "type": "text", + "label": { + "en": "CCU Serial" + }, + "value": "" + } + ] + }, + { + "type": "group", + "label": { + "en": "Device settings" + }, + "children": [ + { + "id": "ShowButtonChannel2", + "type": "checkbox", + "label": { + "en": "Show Button Channel 2" + }, + "value": true + }, + { + "id": "TitleButtonChannel2", + "type": "text", + "label": { + "en": "Title Button Channel 2" + }, + "value": "Schalter Kanal 2" + }, + { + "id": "ShowButtonChannel3", + "type": "checkbox", + "label": { + "en": "Show Button Channel 3" + }, + "value": true + }, + { + "id": "TitleButtonChannel3", + "type": "text", + "label": { + "en": "Title Button Channel 3" + }, + "value": "Schalter Kanal 3" + }, + { + "id": "ShowButtonChannel4", + "type": "checkbox", + "label": { + "en": "Show Button Channel 4" + }, + "value": true + }, + { + "id": "TitleButtonChannel4", + "type": "text", + "label": { + "en": "Title Button Channel 4" + }, + "value": "Schalter Kanal 4" + }, + { + "id": "ShowButtonChannel6", + "type": "checkbox", + "label": { + "en": "Show Button Channel 6" + }, + "value": true + }, + { + "id": "TitleButtonChannel6", + "type": "text", + "label": { + "en": "Title Button Channel 6" + }, + "value": "Schalter Kanal 6" + }, + { + "id": "ShowButtonChannel7", + "type": "checkbox", + "label": { + "en": "Show Button Channel 7" + }, + "value": true + }, + { + "id": "TitleButtonChannel7", + "type": "text", + "label": { + "en": "Title Button Channel 7" + }, + "value": "Schalter Kanal 7" + }, + { + "id": "ShowButtonChannel8", + "type": "checkbox", + "label": { + "en": "Show Button Channel 8" + }, + "value": true + }, + { + "id": "TitleButtonChannel8", + "type": "text", + "label": { + "en": "Title Button Channel 8" + }, + "value": "Schalter Kanal 8" + }, + { + "id": "ShowDimmer", + "type": "checkbox", + "label": { + "en": "Show Dimmer" + }, + "value": true + }, + { + "id": "TitleDimmer", + "type": "text", + "label": { + "en": "Title Dimmer" + }, + "value": "Regler" + }, + { + "id": "QuickActionChannel", + "type": "dropdown", + "label": { + "en": "Quick Action Channel" + }, + "hint": { + "en": "Select which channel should have the Quick Action button." + }, + "value": "onoff.channel2", + "values": [ + { + "id": "onoff.channel2", + "label": { + "en": "Channel 2" + } + }, + { + "id": "onoff.channel3", + "label": { + "en": "Channel 3" + } + }, + { + "id": "onoff.channel4", + "label": { + "en": "Channel 4" + } + }, + { + "id": "onoff.channel6", + "label": { + "en": "Channel 6" + } + }, + { + "id": "onoff.channel7", + "label": { + "en": "Channel 7" + } + }, + { + "id": "onoff.channel8", + "label": { + "en": "Channel 8" + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/drivers/HmIP-MIOB/driver.js b/drivers/HmIP-MIOB/driver.js new file mode 100644 index 0000000..e35b783 --- /dev/null +++ b/drivers/HmIP-MIOB/driver.js @@ -0,0 +1,26 @@ +'use strict'; + +const Homey = require('homey'); +const Driver = require('../../lib/driver.js'); + +class HomematicDriver extends Driver { + + onInit() { + super.onInit(); + this.capabilities = [ + 'onoff.channel2', + 'onoff.channel3', + 'onoff.channel4', + 'onoff.channel6', + 'onoff.channel7', + 'onoff.channel8', + 'dim' + ] + this.homematicTypes = ['HmIP-MIOB']; + this.log(this.homematicTypes.join(','), 'has been inited'); + } + + +} + +module.exports = HomematicDriver; \ No newline at end of file diff --git a/lib/DeviceLister.js b/lib/DeviceLister.js index 28084d0..8e2e7ae 100644 --- a/lib/DeviceLister.js +++ b/lib/DeviceLister.js @@ -1,11 +1,13 @@ 'use strict'; class DeviceLister { - constructor(driver) { + constructor(driver, logger) { this.driver = driver; + this.logger = logger; } - async onListDevices(data, currentBridge) { + async onListDevices(currentBridge) { + this.logger.log('info', 'onListDevices invoked with currentBridge:', currentBridge); if (!currentBridge) { return this.onListDevicesBridges(); } @@ -31,10 +33,12 @@ class DeviceLister { if (!currentBridge) { throw new Error('Missing Bridge'); } - + try { const devices = []; - const bridgeDevices = await currentBridge.listDevices(); + let bridgeDevices; + bridgeDevices = await currentBridge.listDevices(); + for (const [interfaceName, interfaceDevices] of Object.entries(bridgeDevices)) { interfaceDevices.forEach(device => { if (this.driver.homematicTypes.includes(device.TYPE)) { @@ -50,6 +54,7 @@ class DeviceLister { } } }; + if (this.driver.multiDevice) { deviceObj.data.attributes.Index = idx; } @@ -58,12 +63,13 @@ class DeviceLister { } }); } + return devices; } catch (err) { - this.driver.logger.log('error', 'Failed to list devices:', err); - throw new Error('Failed to list devices: ' + err.message); + throw new Error('DeviceLister.onListDevicesDevices() Failed to list devices: ' + err.message); } } + } module.exports = DeviceLister; diff --git a/lib/HomeMaticCCUJack.js b/lib/HomeMaticCCUJack.js index f629929..9f125c8 100644 --- a/lib/HomeMaticCCUJack.js +++ b/lib/HomeMaticCCUJack.js @@ -38,9 +38,19 @@ class HomeMaticCCUJack extends EventEmitter { } enqueueRequest(request) { - this.requestQueue.push(request); - this.processQueue(); + return new Promise((resolve, reject) => { + this.requestQueue.push(async () => { + try { + const result = await request(); + resolve(result); + } catch (err) { + reject(err); + } + }); + this.processQueue(); + }); } + async processQueue() { if (this.isProcessingQueue || this.requestQueue.length === 0) { @@ -159,7 +169,7 @@ class HomeMaticCCUJack extends EventEmitter { }, {}); return deviceMap; } catch (err) { - this.logger.log('error', 'Failed to list devices:', err); + this.logger.log('error', 'HomeMaticCCUJack.listDevices() Failed to list devices:', err); throw err; } } diff --git a/lib/driver.js b/lib/driver.js index be0fb43..f80146e 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -6,9 +6,10 @@ const DeviceLister = require('./DeviceLister'); class Driver extends Homey.Driver { onInit() { + this.logger = this.homey.app.logger; this.multiDevice = false; this.numDevices = 1; - this.deviceLister = new DeviceLister(this); + this.deviceLister = new DeviceLister(this, this.logger); } getDeviceName(address, idx) { @@ -50,9 +51,9 @@ class Driver extends Homey.Driver { session.setHandler('list_devices', async (data) => { try { - return await self.deviceLister.onListDevices(data, currentBridge); + return await self.deviceLister.onListDevices(currentBridge); } catch (err) { - self.homey.app.logger.log('error', 'Failed to list devices during pairing:', err); + self.homey.app.logger.log('error', 'driver.onPair() Failed to list devices during pairing:', err); throw err; } });