Hubitat의 버튼을 HomeKit으로 가져오기

HE에 등록된 버튼 장비들은 Homebridge에 넘어오지 않는다. (ST도 마찬가지이다)
webCore 등의 Hub에서 자동화를 구성하는 것이 자유도가 더 높아 일부러 Homebridge로 연동하지 않는 것도 같고, 다른 장비들과 약간 다른 방식으로 동작하기 때문이기도 한 것 같다.

이번에 HE로 옮겨 오면서 App이나 Driver에 대한 전반적인 이해가 올라가면서 어느정도 코드를 다루게 되어서 버튼을 HomeKit으로 옮겨오기로 했다.
HomeKit에서 지원하는 자동화 정도로 구성이 가능하다면 HomeKit에서 해결하는 것을 선호하기 때문에 버튼을 가져오기로 한 부분도 있다.

HomeKit에서 지원하는 버튼은 버튼 수는 무제한(?)인 듯하고 동작은 원 푸시, 더블 푸시, 롱 푸시 이렇게 3가지 동작이 가능하다. Fibaro 버튼과 같이 3번 이상 눌러 이벤트를 발생시킬 수 있는 버튼의 경우엔 2번 (Double Tap)까지만 이벤트를 받을 수 있다.

Homebridge는 tonesto7의 플러그인을 사용하였다. 아래부터는 javascript의 기본적인(?) 지식은 가지고 있으면 이해하기 쉬울 것이다. 자세하기 설명해두지도 않았기 때문에 무턱대고 수정하기엔 어려운 부분이 있을 수 있다.

accessories/he_st_accessories.js 파일의 HE_ST_Accessory 함수 안의 적당한 위치에 추가한다. js알못이 파일을 보더라도 HE_ST_Accessory함수의 if 구문이 약간의 규칙성이 있을 것이다. 그 규칙성에 맞게 적절히 추가한다.

if (device.capabilities['PushableButton'] !== undefined || device.capabilities['DoubleTapableButton'] !== undefined) {
    that.deviceGroup = 'button';
    platform.log('Button: (' + that.name + ')');

    function getStatelessSwitchProps(single_press, double_press, long_press) {
        var props;
        if (single_press && !double_press && !long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS
            };
        }
        if (single_press && double_press && !long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS
            };
        }
        if (single_press && !double_press && long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.LONG_PRESS,
                validValues: [
                     Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
                     Characteristic.ProgrammableSwitchEvent.LONG_PRESS
                ]
            };
        }
        if (!single_press && double_press && !long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS
            };
        }
        if (!single_press && double_press && long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.LONG_PRESS
            };
        }
        if (!single_press && !double_press && long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.LONG_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.LONG_PRESS
            };
        }
        if (single_press && double_press && long_press) {
            props = {
                minValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
                maxValue: Characteristic.ProgrammableSwitchEvent.LONG_PRESS
            };
        }
        return props;
    }

    const props = getStatelessSwitchProps(device.capabilities['PushableButton'], device.capabilities['DoubleTapableButton'], device.capabilities['HoldableButton']);

    for (let i = 1; i <= device.attributes.numberOfButtons; i++) {
        const thisService = new Service.StatelessProgrammableSwitch(device.name, device.name + i);
        that.getaddService(thisService);

        thisCharacteristic = thisService.getCharacteristic(Characteristic.ProgrammableSwitchEvent)
            .setProps(props);
        platform.addAttributeUsage('pushed', device.deviceid, thisCharacteristic);
        platform.addAttributeUsage('doubleTapped', device.deviceid, thisCharacteristic);
        platform.addAttributeUsage('held', device.deviceid, thisCharacteristic);

        thisCharacteristic = thisService.getCharacteristic(Characteristic.ServiceLabelIndex).setValue(i);
    }
}

위 코드는 Pushable, DoubleTapable, Hodable 버튼 악세서리를 만들어 주는 부분이다.

index.js파일의 HE_ST_Platform prototype을 재정의 하는 부분의 accessories부분의 this.knownCapabilities

    'Routine',
    'PushableButton',
    'DoubleTapableButton',
    'HoldableButton'
];

이렇게 추가해준다. 그 조금 아래부분의 processFieldUpdate를 아래처럼 수정해준다. 아래 방법이 썩 좋은 방법인 것 같진 않지만... 다른 방법으론 떠오르지 않아서 버튼인 경우에 다르게 처리하도록 수정하였다.

processFieldUpdate: function(attributeSet, that) {
    if (!(that.attributeLookup[attributeSet.attribute] && that.attributeLookup[attributeSet.attribute][attributeSet.device])) {
        return;
    }
    var myUsage = that.attributeLookup[attributeSet.attribute][attributeSet.device];
    if (myUsage instanceof Array) {
        for (var j = 0; j < myUsage.length; j++) {
            var accessory = that.deviceLookup[attributeSet.device];
            if (accessory) {
                const buttonService = accessory.services.filter(service => 
                    service.UUID === Service.StatelessProgrammableSwitch.UUID && service.subtype.replace(service.displayName, '') == attributeSet.value
                );

                if (buttonService.length > 0) {
                    var characteristic = buttonService[0].getCharacteristic(Characteristic.ProgrammableSwitchEvent)

                    var event = Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS;
                    switch (attributeSet.attribute) {
                        case 'doubleTapped':
                            event = Characteristic.ProgrammableSwitchEvent.DOUBLE_PRESS;
                            break;
                        case 'held':
                            event = Characteristic.ProgrammableSwitchEvent.LONG_PRESS;
                            break;
                    }
                    characteristic.setValue(event);
                } else {
                    accessory.device.attributes[attributeSet.attribute] = attributeSet.value;
                    myUsage[j].getValue();
                }
            }
        }
    }
}

이렇게 수정해주면 버튼을 누를 때 HomeKit으로 이벤트가 전달된다.
HomeKit의 자동화 룰 구성은 쉬우니까 따로 설명하지 않는다. Home앱보다 Eve앱을 이용하여 룰을 구성하는 것이 더 좋을 것이다.
트리거 + 컨디션을 지정할 수 있기 때문에 토글 버튼을 만들 수 있다. 버튼 눌림(트리거) + 켜진 상태(컨디션), 버튼 눌림(트리거) + 꺼진 상태(컨디션)로 룰을 두가지로 구성한다면 토글이 가능하다.

이렇게 이미 3rd party앱에서 다양하게 룰 생성이 가능한데 왜 애플은 직접 만들어주지 않는 것일지 모르겠다. 시리와 더불어 IOT기능에도 좀더 힘을 실어줬으면 좋겠다.

이렇게 해서 HE의 버튼을 HomeKit으로 가져왔다. 사실 webCore에서 더 편리하게 룰을 구성할 수 있긴하다. 게다가 HE는 모두 로컬에서 동작하기 때문에 HomeKit에서 룰을 구성할 때보다 더 안정적으로 사용이 가능하다. 뭔가 단점이 더 많은 것 같지만 HomeKit에서 버튼을 구성할 수 있는 것으로 만족한다.

Show Comments