diff --git a/README.md b/README.md index 8aa2b1d..785e297 100644 --- a/README.md +++ b/README.md @@ -384,7 +384,7 @@ Feel free to create [Issue](https://github.com/ilcato/homebridge-fibaro-home-cen ### Version 3.1.0 -- Added support for remote scene controller (like Aeon Labs Minimote). +- Initial support for remote controller (tested with Aeon Labs Minimote, Fibaro Button). - Dependencies updates. ### Version 3.0.0 diff --git a/src/constants.ts b/src/constants.ts index 83cb098..c4c9e76 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -76,8 +76,8 @@ export const SUBTYPE_CLIMATE_ZONE = 'CZ'; export const SUBTYPE_HEATING_ZONE = 'HZ'; export const SUBTYPE_OPEN_CLOSE_ONLY = 'OPENCLOSEONLY'; export const SUBTYPE_PM2_5 = 'PM2_5'; -export const SUBTYPE_REMOTE_CONTROLLER = 'RC'; -export const SUBTYPE_REMOTE_SCENE_CONTROLLER = 'RS'; +export const SUBTYPE_REMOTE_CONTROLLER_CENTRAL_SCENE = 'CS'; +export const SUBTYPE_REMOTE_CONTROLLER_SCENE_ACTIVATION = 'SA'; // Configuration export const CONFIG_LOGS_LEVEL_VERBOSE = 2; diff --git a/src/deviceAuto.ts b/src/deviceAuto.ts index 846ea42..0f70361 100644 --- a/src/deviceAuto.ts +++ b/src/deviceAuto.ts @@ -360,21 +360,35 @@ export class DeviceConfigurations { // Remote controller and remote scene controller @DeviceType('com.fibaro.remoteController') @DeviceType('com.fibaro.remoteSceneController') + @DeviceType(/^com\.fibaro\.FGPB/) private static remoteController(Service, Characteristic, device) { const availableScenes = device.properties.availableScenes || []; - if (availableScenes.length === 0) { // Aeon Minimote like devices - return; - } - const numberOfButtons = Math.ceil(availableScenes.length / 2); // Assuming each button can trigger 2 scenes + const centralSceneSupport = device.properties.centralSceneSupport || []; + if (availableScenes.length > 0) { // Aeon Minimote like devices + const numberOfButtons = Math.ceil(availableScenes.length / 2); // Assuming each button can trigger 2 scenes - return Array.from({ length: numberOfButtons }, (_, index) => ({ - service: Service.StatelessProgrammableSwitch, - characteristics: [ - Characteristic.ProgrammableSwitchEvent, - Characteristic.ServiceLabelIndex, - ], - subtype: `${device.id}---RS-${index + 1}`, - })); + return Array.from({ length: numberOfButtons }, (_, index) => ({ + service: Service.StatelessProgrammableSwitch, + characteristics: [ + Characteristic.ProgrammableSwitchEvent, + Characteristic.ServiceLabelIndex, + ], + subtype: `${device.id}---${constants.SUBTYPE_REMOTE_CONTROLLER_SCENE_ACTIVATION}-${index + 1}`, + })); + } else if (centralSceneSupport.length > 0) { // Fibaro button like devices + const parsedCentralSceneSupport = JSON.parse(centralSceneSupport); + + return parsedCentralSceneSupport.map(keys => ({ + service: Service.StatelessProgrammableSwitch, + characteristics: [ + Characteristic.ProgrammableSwitchEvent, + Characteristic.ServiceLabelIndex, + ], + subtype: `${device.id}---${constants.SUBTYPE_REMOTE_CONTROLLER_CENTRAL_SCENE}-${keys.keyId}`, + })); + } + // No supported configuration is found + return; } // Security system diff --git a/src/fibaroAccessory.ts b/src/fibaroAccessory.ts index 6ead2bd..6ebd178 100644 --- a/src/fibaroAccessory.ts +++ b/src/fibaroAccessory.ts @@ -193,8 +193,8 @@ export class FibaroAccessory { service.isHeatingZone = IDs.length >= 3 && IDs[2] === constants.SUBTYPE_HEATING_ZONE; service.isOpenCloseOnly = IDs.length >= 3 && IDs[2] === constants.SUBTYPE_OPEN_CLOSE_ONLY; service.isPM2_5Sensor = IDs.length >= 3 && IDs[2] === constants.SUBTYPE_PM2_5; - service.isRemoteController = IDs.length >= 5 && IDs[3] === constants.SUBTYPE_REMOTE_CONTROLLER; - service.isRemoteSceneController = IDs.length >= 5 && IDs[3] === constants.SUBTYPE_REMOTE_SCENE_CONTROLLER; + service.isRemoteControllerCentralScene = IDs.length >= 5 && IDs[3] === constants.SUBTYPE_REMOTE_CONTROLLER_CENTRAL_SCENE; + service.isRemoteControllerSceneActivation = IDs.length >= 5 && IDs[3] === constants.SUBTYPE_REMOTE_CONTROLLER_SCENE_ACTIVATION; service.remoteButtonNumber = IDs.length >= 5 ? parseInt(IDs[4]) : -1; } diff --git a/src/getFunctions.ts b/src/getFunctions.ts index 0303585..315a350 100644 --- a/src/getFunctions.ts +++ b/src/getFunctions.ts @@ -428,15 +428,26 @@ export class GetFunctions { @characteristicGetter(Characteristics.ProgrammableSwitchEvent) getProgrammableSwitchEvent(characteristic, service, _IDs, properties) { - if (service.isRemoteSceneController) { + if (service.isRemoteControllerSceneActivation) { if (properties.value % 2 === 1) { // 1 for single press, 0 for double press characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); } else { characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.LONG_PRESS); } - } else if (service.isRemoteController) { - // TODO: Add support for remote controller - return; + } else if (service.isRemoteControllerCentralScene) { + switch (properties.value) { + case 'Pressed': + characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); + break; + case 'Pressed2': + characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS); + break; + case 'HeldDown': + characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.LONG_PRESS); + break; + default: + return; + } } } diff --git a/src/pollerupdate.ts b/src/pollerupdate.ts index 38f5596..3ff7547 100644 --- a/src/pollerupdate.ts +++ b/src/pollerupdate.ts @@ -67,18 +67,30 @@ export class Poller { private handleEvent(event) { const { type, data } = event; const deviceId = data.deviceId || 'N/A'; - const sceneId = data.sceneId || 'N/A'; - // Create a change object in order to reuse the manageValue function - const change = { id: deviceId, value: sceneId }; switch (type) { - case 'SceneActivationEvent': + case 'SceneActivationEvent': { + const sceneId = data.sceneId || 'N/A'; + // Create a change object in order to reuse the manageValue function + const change = { id: deviceId, value: sceneId }; this.manageValue(change); + this.platform.log.info(`Processed event scene-${sceneId} of type ${type} from device ${deviceId}`); break; + } + case 'CentralSceneEvent': { + // key number pressed, starting from 1 + const keyId = data.keyId || 'N/A'; + // key pressed mode: "Pressed" for single press, "Pressed2" for double press, "HeldDown" for long press + const keyAttribute = data.keyAttribute || 'N/A'; + // Create a change object in order to reuse the manageValue function + const change = { id: deviceId, value: keyAttribute, button: keyId }; + this.manageValue(change); + this.platform.log.info(`Processed event ${keyAttribute} of type ${type} from device ${deviceId} key ${keyId}`); + break; + } default: return; } - this.platform.log.info(`Processed event ${sceneId} of type ${type} from device ${deviceId}`); } private handleChange(change) { @@ -138,14 +150,22 @@ export class Poller { change.value = (change.value - 32) * 5 / 9; } - if (service.isRemoteSceneController) { - const buttonNumber = service.remoteButtonNumber; - const eventNumber = change.value; - // Each button handles two consecutive events starting from button 1. Skip if not in range. - if (!(eventNumber >= (buttonNumber - 1) * 2 + 1 && eventNumber <= buttonNumber * 2) || - characteristic.constructor !== this.platform.Characteristic.ProgrammableSwitchEvent) { + if (service.remoteButtonNumber) { + if (characteristic.constructor !== this.platform.Characteristic.ProgrammableSwitchEvent) { return; } + if (service.isRemoteControllerSceneActivation) { + const buttonNumber = service.remoteButtonNumber; + const eventNumber = change.value; + // Each button handles two consecutive events starting from button 1. Skip if not in range. + if (!(eventNumber >= (buttonNumber - 1) * 2 + 1 && eventNumber <= buttonNumber * 2)) { + return; + } + } else if (service.isRemoteControllerCentralScene) { + if (service.remoteButtonNumber !== change.button) { + return; + } + } } const getFunction = this.platform.getFunctions!.getFunctionsMapping.get(characteristic.constructor);