Skip to content

Commit

Permalink
Added support for Fibaro Button
Browse files Browse the repository at this point in the history
  • Loading branch information
ilcato committed Oct 13, 2024
1 parent 10eeaf6 commit 61d4483
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
38 changes: 26 additions & 12 deletions src/deviceAuto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/fibaroAccessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
19 changes: 15 additions & 4 deletions src/getFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down
42 changes: 31 additions & 11 deletions src/pollerupdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 61d4483

Please sign in to comment.