YOGYUI

현대통신 월패드 주방 비디오폰 세대현관문/공동현관문 자동 열림 기능 추가 본문

홈네트워크(IoT)/힐스테이트 광교산

현대통신 월패드 주방 비디오폰 세대현관문/공동현관문 자동 열림 기능 추가

요겨 2024. 10. 24. 23:34
반응형

Hyundai HT Wallpad: Automatic Open Front/Communal Door Function (Kitchen Subphone)

요근래 1주일간 블로그에 달린 댓글들이 나를 아주 기분좋게 만들었다 ^^

https://yogyui.tistory.com/387/#comment24701007
https://yogyui.tistory.com/674/#comment24701075
https://yogyui.tistory.com/609/#comment24701727

 

특히 '현대통신 월패드 RS-485 통신 연동 홈어시스턴트 애드온'은 큰 문제없이 잘 작동하고 있다고 하니 꽤나 뿌듯하다

기분이 좋아진 김에, "언젠가는 해야지..." 하고 미뤄뒀던 추가 기능 개발을 하루만에 해치워버렸다 ㅋㅋ

힐스테이트 광교산::주방 비디오폰 세대현관문/공동현관문 기능 분리 (HomeAssistant)

 

힐스테이트 광교산::주방 비디오폰 세대현관문/공동현관문 기능 분리 (HomeAssistant)

현대통신 RS-485 연동코드: 주방 비디오폰 세대현관문/공동현관문 호출 상태 및 문열기 명령 기능 분리 힐스테이트 광교산에서 사용하려고 만든 현대통신 RS-485 연동 코드가 이래저래 입소문(?)을

yogyui.tistory.com

주방 비디오폰 연동 관련 글이 반응이 나름 좋았는데, "세대/공동현관문 자동 열림" 기능은 홈어시스턴트(HA)나 애플 홈킷, 구글 홈 등 IoT 플랫폼의 자동화나 씬(scene)을 통해 구현을 해야만 했다

구글 홈이나 애플 홈킷 등 홈 IoT 플랫폼에서 별도로 사용자가 자동화를 구성할 필요없이 간단하게 현관문이 호출된 후 일정 시간이 지나면 자동으로 열어주는 기능을 활성화/비활성화할 수 있게 엔티티(혹은 액세서리)를 추가해봤다

※ 개인적으로 자동 열림 기능은 배달 음식 시켜먹을 때 아주 유용하게 써먹고 있다

1. 요구사항

  • 구글 홈, 애플 홈킷 등 홈 IoT 플랫폼에서 손쉽게 자동열림 기능을 켜고 끌 수 있어야 한다
  • 세대현관문과 공동현관문의 자동 열림 기능은 서로 분리되어야 한다
  • 현관문이 호출된 후 일정 시간이 지나면 자동으로 열어줘야 한다

2. 코드 구현

위에서 정리한 요구사항들을 바탕으로 간단하게 코드 몇줄을 추가해줬다

2.1. 소스코드 변경 사항

현관문이 호출된 후 일정 시간이 지나면 종료되면서 콜백을 호출하는 쓰레드를 다음과 같이 구현했다

## Subphone.py
import threading

class ThreadAutoOpenDoor(threading.Thread):
    _keepAlive: bool = True
    
    def __init__(self, interval_sec: float):
        threading.Thread.__init__(self)
        self.interval_sec = interval_sec
        self.sig_terminated = Callback(bool)
    
    def run(self):
        tm_start = time.perf_counter()
        while self._keepAlive:
            elapsed = time.perf_counter() - tm_start
            if elapsed >= self.interval_sec:
                break
            time.sleep(100e-3)
        self.sig_terminated.emit(self._keepAlive)
    
    def stop(self):
        self._keepAlive = False

세대현관문, 공동현관문 각각 자동열림 기능을 활성화할 지 여부 및 몇 초가 지난 뒤 열지(인터벌 시간)를 결정하는 파라미터는 Subphone 객체 내부 멤버변수로 지정했다 (인터벌 default값은 3초로 설정.. 너무 바로 여는 것보다는 어느 정도 여유를 두고 여는게 약간 자연스러워보인다고나 할까? ㅋㅋ)

class SubPhone(Device):
    # 세대/공동현관문 자동 열림 기능
    enable_auto_open_front_door: bool = False
    auto_open_front_door_interval_sec: float = 3
    enable_auto_open_communal_door: bool = False
    auto_open_communal_door_interval_sec: float = 3
    
    def __init__(self, name: str = 'SubPhone', index: int = 0, room_index: int = 0):
        self._thread_auto_open_front_door: Union[ThreadAutoOpenDoor, None] = None
        self._thread_auto_open_communal_door: Union[ThreadAutoOpenDoor, None] = None
        self.sig_open_front_door = Callback(int, int)
        self.sig_open_communal_door = Callback(int, int)

현관문 각각 쓰레드 시작/종료 및 콜백 처리 메서드도 적절하게 구현

class SubPhone(Device):
    def _startThreadAutoOpenFrontDoor(self):
        if self._thread_auto_open_front_door is None:
            self._thread_auto_open_front_door = ThreadAutoOpenDoor(self.auto_open_front_door_interval_sec)
            self._thread_auto_open_front_door.sig_terminated.connect(self._onThreadAutoOpenFrontDoorTerminated)
            self._thread_auto_open_front_door.daemon = True
            self._thread_auto_open_front_door.start()
            writeLog('Auto open front door thread started', self)
            
    def _stopThreadAutoOpenFrontDoor(self):
        if self._thread_auto_open_front_door is not None:
            self._thread_auto_open_front_door.stop()
            
    def _onThreadAutoOpenFrontDoorTerminated(self, command: bool):
        del self._thread_auto_open_front_door
        self._thread_auto_open_front_door = None
        writeLog('Auto open front door thread terminated', self)
        if command:
            self.sig_open_front_door.emit(self.index, self.room_index)
            
    def _startThreadAutoOpenCommunalDoor(self):
        if self._thread_auto_open_communal_door is None:
            self._thread_auto_open_communal_door = ThreadAutoOpenDoor(self.auto_open_communal_door_interval_sec)
            self._thread_auto_open_communal_door.sig_terminated.connect(self._onThreadAutoOpenCommunalDoorTerminated)
            self._thread_auto_open_communal_door.daemon = True
            self._thread_auto_open_communal_door.start()
            writeLog('Auto open communal door thread started', self)
    
    def _stopThreadAutoOpenCommunalDoor(self):
        if self._thread_auto_open_communal_door is not None:
            self._thread_auto_open_communal_door.stop()
            
    def _onThreadAutoOpenCommunalDoorTerminated(self, command: bool):
        del self._thread_auto_open_communal_door
        self._thread_auto_open_communal_door = None
        writeLog('Auto open communal door thread terminated', self)
        if command:
            self.sig_open_communal_door.emit(self.index, self.room_index)

현관문의 호출 상태가 변했을 때, 자동열림 기능이 활성화되어 있는 경우 인터벌 체크 쓰레드를 구동하도록 다음과 같이 코드를 추가해줬다

class Subphone(Device):
    def updateState(self, _: int, **kwargs):
        ringing_front = kwargs.get('ringing_front')
        if ringing_front is not None:
            if ringing_front:
                self.state_ringing = StateRinging.FRONT
                self.state_ringing_front = 1
                if self.enable_auto_open_front_door:
                    self._startThreadAutoOpenFrontDoor()
            else:
                self.state_ringing = StateRinging.IDLE
                self.state_ringing_front = 0
                self._stopThreadAutoOpenFrontDoor()

        ringing_communal = kwargs.get('ringing_communal')
        if ringing_communal is not None:
            if ringing_communal:
                self.state_ringing = StateRinging.COMMUNAL
                self.state_ringring_communal = 1
                if self.enable_auto_open_communal_door:
                    self._startThreadAutoOpenCommunalDoor()
            else:
                self.state_ringing = StateRinging.IDLE
                self.state_ringring_communal = 0
                self._stopThreadAutoOpenCommunalDoor()

이 외에 콜백 처리 구문이나 활성화 여부 등의 코드는 Home 객체에 구현해뒀는데, 아래 깃허브 커밋을 보면 구현 방법을 상세히 알 수 있다

2.2. MQTT 사양

(1) 현재 상태를 외부로 publish 

Topic: home/state/subphone/0/0/autoopen
Payload: {
    "enable_auto_open_front": int,
    "auto_open_front_interval": float,
    "enable_auto_open_communal": int,
    "auto_open_communal_interval": float
}

 

(2) 상태 변경 명령 subscribe

Topic: home/command/subphone/0/0
Payload: 
- {"enable_auto_open_front": int}
- {"auto_open_front_interval": float}
- {"enable_auto_open_communal": int}
- {"auto_open_communal_interval": float}
(4종의 페이로드를 한꺼번에 보내도 동작함)

2.3. GitHub Commit

commit id: fc6e14e8f7c83e5108b9efef84ee9097d3348de4

https://github.com/YOGYUI/HomeNetwork/commit/fc6e14e8f7c83e5108b9efef84ee9097d3348de4

 

주방 비디오폰 - 세대/공동현관문 자동열림 기능 추가 · YOGYUI/HomeNetwork@fc6e14e

YOGYUI committed Oct 24, 2024

github.com

3. 홈IoT 플랫폼 연동

구글홈, 애플홈, HomeAssistant 모두 자동열림 기능을 간단하게 활성화/비활성화하기 위해 스위치 형태로 액세서리 혹은 엔티티를 추가해주기로 결정!

3.1. Homeassistant 엔티티 추가 (auto discovery)

HA 사용자의 경우 별도로 configuration.yaml 파일을 수정할 필요없이 자동으로 엔티티가 추가될 수 있는 MQTT discovery 기능을 사용하면 된다

Subphone.py 내부에 discovery용 토픽 및 페이로드 발행 코드를 아래와 같이 구현했다

class Subphone(Device):
    def configMQTT(self, retain: bool = False):
        topic = f'{self.ha_discovery_prefix}/switch/{self.unique_id}_auto_open_front/config'
        obj = {
            "name": self.name + " Auto Open (Front)",
            "object_id": self.unique_id + "_auto_open_front",
            "unique_id": self.unique_id + "_auto_open_front",
            "state_topic": self.mqtt_publish_topic + '/autoopen',
            "command_topic": self.mqtt_subscribe_topic,
            "value_template": '{ "enable_auto_open_front": {{ value_json.enable_auto_open_front }} }',
            "payload_on": '{ "enable_auto_open_front": 1 }',
            "payload_off": '{ "enable_auto_open_front": 0 }',
            "icon": "mdi:door-open"
        }
        self.mqtt_client.publish(topic, json.dumps(obj), 1)

        topic = f'{self.ha_discovery_prefix}/switch/{self.unique_id}_auto_open_communal/config'
        obj = {
            "name": self.name + " Auto Open (Communal)",
            "object_id": self.unique_id + "_auto_open_communal",
            "unique_id": self.unique_id + "_auto_open_communal",
            "state_topic": self.mqtt_publish_topic + '/autoopen',
            "command_topic": self.mqtt_subscribe_topic,
            "value_template": '{ "enable_auto_open_communal": {{ value_json.enable_auto_open_communal }} }',
            "payload_on": '{ "enable_auto_open_communal": 1 }',
            "payload_off": '{ "enable_auto_open_communal": 0 }',
            "icon": "mdi:door-open"
        }
        self.mqtt_client.publish(topic, json.dumps(obj), 1)

자동으로 추가되는 엔티티는 아래와 같이 각각

- subphone_0_0_auto_open_front

- subphone_0_0_auto_open_communal

고유 아이디를 가진 엔티티가 추가된다 (디스플레이 이름은 적절하게 바꿔주면 된다)

대시보드에 현관문 관련 구성요소만 모아놓으면 나름 그럴듯해보인다

3.2. Homebridge 액세서리 추가

안타깝게도 Homebridge 액세서리를 자동으로 추가하는 기능은 찾아보지도 않았다 ㅋㅋ

아직까지도 우직하게 json 파일에 무식하게 추가해주고있다 ^^;;

{
    "accessories": [
        {
            "accessory": "mqttthing",
            "type": "switch",
            "name": "Auto Open Front Door(MQTT)",
            "topics": {
                "getOn": {
                    "topic": "home/state/subphone/0/0/autoopen",
                    "apply": "return (JSON.parse(message).enable_auto_open_front == 1);"
                },
                "setOn": {
                    "topic": "home/command/subphone/0/0",
                    "apply": "return JSON.stringify({enable_auto_open_front: message});"
                }
            },
            "integerValue": true,
            "onValue": 1,
            "offValue": 0,
            "logMqtt": false
        },
        {
            "accessory": "mqttthing",
            "type": "switch",
            "name": "Auto Open Communal Door(MQTT)",
            "topics": {
                "getOn": {
                    "topic": "home/state/subphone/0/0/autoopen",
                    "apply": "return (JSON.parse(message).enable_auto_open_communal == 1);"
                },
                "setOn": {
                    "topic": "home/command/subphone/0/0",
                    "apply": "return JSON.stringify({enable_auto_open_communal: message});"
                }
            },
            "integerValue": true,
            "onValue": 1,
            "offValue": 0,
            "logMqtt": false
        }
    ]
}

추가 후 애플 홈에 추가된 2개의 스위치를 적절히 이름을 바꾸고 '현관'방에 배치해줬다

Siri를 통해 자동열림 스위치를 손쉽게 음성으로 켜고 끌 수 있다

3.3. Homeassistant 애드온 버전 업데이트

자동 열림 인터벌은 HA 애드온 설정을 통해 변경 가능하도록 애드온 파라미터를 추가해줬다 (따라서 애드온 버전 업데이트가 필요!)

※ 업데이트된 버전은 1.1.4

※ 물론 MQTT를 통해서도 변경 가능하게 구현은 해뒀다 ㅎㅎ 관련 토픽/페이로드는 소스코드를 참고하면 된다

1.1.3 버전 업데이트 시기가 8월 22일이었으니 거의 2달만에 이뤄진 업데이트!

 

파라미터 설명도 나름 상세히 추가해뒀다 

4. 테스트

## 공동현관문 자동 열림 기능 활성화
<18:11:19.785> [SubPhone (0xB2791510)] Set enable auto open communal door: True
<18:11:47.112> [PacketParser (0xB2788A50)] <SUBPHONE> 7F 5A 00 00 EE >> Communal door ringing started
## 공동현관문 호출 시, 자동 열림 기능 활성화에 따른 인터벌 체크 쓰레드 시작
<18:11:47.113> [SubPhone (0xB2791510)] Auto open communal door thread started
<18:11:47.113> [SubPhone (0xB2791510)] Ringing: COMMUNAL
## 설정된 인터벌(3초) 후 쓰레드 종료
<18:11:50.123> [SubPhone (0xB2791510)] Auto open communal door thread terminated
## 쓰레드 종료 후 주방 비디오폰 '공동현관문 열기' 패킷 전송
<18:11:50.123> [ThreadCommandQueue (0xB1CFF400)] Get Command Queue: {
    device: <Kitchen Subphone, Dev Idx: 0, Room Idx: 0>
    category: lock_communal target: Unsecured
    parser: <SUBPHONE(PacketParser at 0xb2788a50)>
}
<18:11:50.124> [SubPhone (0xB2791510)] Lock Communal: Unsecured
<18:11:50.137> [PacketParser (0xB2788A50)] <SUBPHONE> Send >> 7F 5F 00 00 EE
<18:11:50.334> [PacketParser (0xB2788A50)] <SUBPHONE> Send >> 7F 61 00 00 EE
<18:11:50.535> [PacketParser (0xB2788A50)] <SUBPHONE> Send >> 7F 60 00 00 EE
<18:11:54.060> [PacketParser (0xB2788A50)] <SUBPHONE> 7F 5E 00 00 EE >> Streaming finished
<18:11:54.061> [SubPhone (0xB2791510)] Streaming: False
<18:11:54.061> [SubPhone (0xB2791510)] Ringing: IDLE
<18:11:54.061> [SubPhone (0xB2791510)] Lock Communal: Secured

설정한 시간 후 자동으로 열리는 것을 확인!

동영상도 찍어서 같이 올리고 싶었는데, 호출기에 아파트 동-호수가 고스란히 찍히기 때문에 패스~~ ㅎㅎ

 

이번 구현 결과물의 장점을 하나 꼽아본다면, 홈IoT 플랫폼의 네트워크 기반 자동화에 의한 자동 열림보다는 좀 더 반응이 빠르다는 점? ^^ (로컬 월패드-주방 비디오폰 간 RS-485 통신 라인을 그대로 이용하기 때문!)


이렇게 또 하나의 기능이 아주 스무스하게 추가되었다

역시 칭찬은 고래도 춤추게 한다 ㅎㅎ

반응형