YOGYUI

힐스테이트 광교산::현관 도어락 - 애플 홈킷 + 구글 어시스턴트 연동 본문

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

힐스테이트 광교산::현관 도어락 - 애플 홈킷 + 구글 어시스턴트 연동

요겨 2022. 6. 26. 18:34
반응형

 

거실 월패드에서는 비디오폰을 통화를 하거나, 현관 출입문 도어락을 해제할 수 있다 (문열기)

문열기 버튼을 클릭했을 때, 조명과 아울렛이 연결된 RS-485 포트에서 처음보는 패킷을 캡쳐할 수 있다

class ParserLight(SerialParser):
    def interpretPacket(self, packet: bytearray):
        try:
            if packet[3] == 0x19:  # 조명
                self.handleLight(packet)
            elif packet[3] == 0x1F:  # 아울렛 (콘센트)
                self.handleOutlet(packet)
            else:
                writeLog(f'Unknown packet: {self.prettifyPacket(packet)}', self)
        except Exception as e:
            writeLog('interpretPacket::Exception::{} ({})'.format(e, packet), self)

<14:19:08.704579> [ParserLight] Unknown packet: F7 0E 01 1E 02 43 11 04 00 04 FF FF B6 EE
<14:19:08.991658> [ParserLight] Unknown packet: F7 0E 01 1E 02 43 11 04 00 04 FF FF B6 EE


패킷의 4번째 바이트가 0x1E인 명령 패킷이 약 300ms 간격을 두고 두번 반복해서 캡쳐되었다

그런데...

아무리 캡쳐된 패킷을 전송해봐도 현관 도어락이 해제되지 않았다 ㅠ_ㅠ

패킷간 인터벌까지 100ms~500ms까지 1ms 단위로 바꿔가면서 이리저리 삽질을 해봤는데도 아무런 반응이 없었다

 

결국 RS-485 방식으로는 제어가 되지 않는 것 같아 월패드를 다시 뜯어봤다

1. 도어락 무선 송수신 모듈 신호 확인

월패드에 너무나 익숙한 게이트맨도어락 연동기가 연결되어 있는 것을 알 수 있었다!

부리나케 현관으로 달려가 도어락을 확인해보니

아니나다를까 게이트맨의 ASSA ABLOY(?) 제품이 설치되어 있다 (이제서야 확인하다니 ㅋㅋ)

 

회사 홈페이지를 가보니 G-SUIT scan이라는 제품명으로 판매가 되고 있다

(57만원이라니... 더럽게도 비싼거 달아놨네 ㅋㅋㅋ)

나름 지문인식 제품이라 편하게 사용하고 있다

블루투스 지원도 된다고 하는데, 기능을 활용하려면 브릿지를 따로 사야한다고 한다... 굳이 살 필요는 없겠지? ㅎㅎ

https://www.gatemanshop.com/krir-bdl-g-suit-scan

 

 

도어락 건전지 삽입부 아래를 보면 무선 통신 모듈(스마트리빙팩)이 장착되어 있는 것을 볼 수 있다

이녀석이 월패드에 장착된 연동기랑 무선으로 통신을 하면서 도어락을 해제하는 구조로 보인다

 

연동기와 연결된 4pin 신호선은 다행히도 터미널블록을 통해 월패드와 연결되어 있어, 멀티미터나 오실로스코프 측정이 용이하다 (연결된 선 색은 위에서부터 빨간색, 검은색, 흰색, 노란색)

 

빨간색과 검은색 선은 전원과 접지(ground)로 사용되는 것이 일반적이라 멀티미터로 전원을 측정해보니 DC +12V로 값이 나왔다 ※ 나중에 외부 전원 필요한 모듈 사용할 때 이 12V를 사용하는 걸 고려해봐야겠다

 

그리고 흰색선과 노란색 선은 정체를 알 수 없어 오실로스코프로 연결 후 파형을 측정해봤다

아무런 조작을 하지 않았을 때, 노란색 선은 약 +3V 레벨로 측정되었으며, 흰색 선은 0V 레벨로 측정되었다

(이사하면서 USB 플래시 메모리를 잃어버렸는지, 아무리 찾아봐도 없어서 파형은 사진으로 대체 ㅠㅠ)

 

그리고 월패드에서 문열기 버튼을 눌렀을 때 트리거를 잡아보면

노란색 선의 전압 레벨이 0V로 내려갔다가 약 1초 후 다시 +3V로 복귀하는 것을 알 수 있었다

 

시간축을 좀 더 확대해보면

노란색 선의 신호가 0으로 떨어진 뒤, 약 27us 뒤 다시 살짝 올라갔다가 약 30us 뒤 다시 0으로 떨어지는 아주 기묘한 파형을 볼 수 있다 (뭔가 시리얼 통신이 이루어지는 느낌이 아니긴 한데...)

 

2. 도어락 해제 GPIO 제어 코드 작성

측정한 파형을 토대로, 광교 아이파크때와 마찬가지로 라즈베리파이의 GPIO를 노란색 선에 연결해 이리저리 제어해봤다

그 결과, 두번 정도만 Positive Edge 트리거를 잡을 수 있게 해주면 문이 열리는 것을 알 수 있었다

import json
from Device import *
import threading
import RPi.GPIO as GPIO
from Common import Callback, writeLog

class ThreadDoorLockOpen(threading.Thread):
    def __init__(self, gpio_port: int):
        threading.Thread.__init__(self)
        self.gpio_port = gpio_port
        self.sig_terminated = Callback()
    
    def run(self):
        writeLog('Started', self)
        GPIO.output(self.gpio_port, GPIO.LOW)
        for _ in range(2):
            writeLog(f"Set GPIO PIN{self.gpio_port} as LOW", self)
            time.sleep(0.1)
            GPIO.output(self.gpio_port, GPIO.HIGH)
            writeLog(f"Set GPIO PIN{self.gpio_port} as HIGH", self)
            time.sleep(0.1)
        time.sleep(5)  # 5초간 Unsecured state를 유지해준다
        writeLog('Terminated', self)
        self.sig_terminated.emit()

class DoorLock(Device):
    enable: bool = False
    gpio_port: int = 0
    thread_open: Union[ThreadDoorLockOpen, None] = None

    def __init__(self, name: str = 'Doorlock', **kwargs):
        super().__init__(name, **kwargs)
        self.state = 1
        self.setParams(True, 23)
    
    def __repr__(self):
        repr_txt = f'<{self.name}({self.__class__.__name__} at {hex(id(self))})'
        repr_txt += '>'
        return repr_txt
    
    def publish_mqtt(self):
        # 'Unsecured', 'Secured', 'Jammed', 'Unknown'
        state_str = 'Unknown'
        if self.state == 0:
            state_str = 'Unsecured'
        elif self.state == 1:
            state_str = 'Secured'
        obj = {"state": state_str}
        if self.mqtt_client is not None:
            self.mqtt_client.publish(self.mqtt_publish_topic, json.dumps(obj), 1)
    
    def setParams(self, enable: bool, gpio_port: int):
        self.enable = enable
        self.gpio_port = gpio_port
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(self.gpio_port, GPIO.IN, GPIO.PUD_UP)  # GPIO IN, Pull Down 설정

    def updateState(self, state: int, **kwargs):
        self.state = state
        if not self.init:
            self.publish_mqtt()
            self.init = True
        if self.state != self.state_prev:
            self.publish_mqtt()
        self.state_prev = self.state

    def startThreadOpen(self):
        if self.thread_open is None:
            self.state = 0
            GPIO.setup(self.gpio_port, GPIO.OUT)
            GPIO.output(self.gpio_port, GPIO.HIGH)
            self.thread_open = ThreadDoorLockOpen(self.gpio_port)
            self.thread_open.sig_terminated.connect(self.onThreadOpenTerminated)
            self.thread_open.start()
        else:
            writeLog('Thread is still working', self)

    def onThreadOpenTerminated(self):
        del self.thread_open
        self.thread_open = None
        self.state = 1
        self.publish_mqtt()
        GPIO.setup(self.gpio_port, GPIO.IN, GPIO.PUD_UP)  # GPIO IN, Pull Down 설정

    def open(self):
        if self.enable:
            self.startThreadOpen()
        else:
            writeLog('Disabled!', self)

광교 아이파크때와 마찬가지로 평소에는 GPIO 핀을 INPUT, Pull-up 모드로 설정한 뒤 (평소에는 3V니깐~), 제어하고자 할 때는 OUTPUT 모드로 변경하도록 구현했다

또한, open 명령이 왔을 때 메인 이벤트 루프가 아닌 별도의 쓰레드에서 LOW-HIGH 설정 동작을 2회 반복하도록 구현했다

(그냥 0.1초 간격으로 충분히 긴 간격으로 두번 LOW-HIGH해도 정상적으로 동작하는걸 실험을 통해 확인했다)

 

3. Homebridge 액세서리 설정

광교 아이파크때는 단순한 switch로 설정했었는데, 여기서는 좀 다른 시도를 해보고 싶어 Lock Mechanism 항목을 사용해보기로 했다

매뉴얼을 따라서 간단하게 다음과 같이 액세서리를 구성해줬다

{
    "accessory": "mqttthing",
    "type": "lockMechanism",
    "name": "Doorlock (MQTT)",
    "url": "mosquitto broker url",
    "username": "mosquitto auth id",
    "password": "mosquitto auth password",
    "topics": {
        "getLockCurrentState": {
            "topic": "home/hillstate/doorlock/state",
            "apply": "return JSON.parse(message).state;"
        },
        "getLockTargetState": {
            "topic": "home/hillstate/doorlock/state",
            "apply": "return JSON.parse(message).state;"
        },
        "setLockTargetState": {
            "topic": "home/hillstate/doorlock/command",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "lockValues": [
        "Unsecured",
        "Secured",
        "Jammed",
        "Unknown"
    ],
    "logMqtt": true
}

안타깝게도 연동기를 통해서는 현재 도어락의 잠김/열림/걸림 상태를 확인할 수 있는 방법이 없어서 명령을 통해 제어 가능한 상태는 Unsecured가 전부이다 ㅠㅠ (열기만 하면 되니 기능상 큰 불편함을 없을 것 같긴 한데... 문이 제대로 안닫힌 상태인 것을 확인할 수 있으면 참 좋을텐데 아쉽다)

 

이에 맞춰서 소스코드(MQTT 메시지 핸들러, 명령 큐 쓰레드)도 수정해줬다

소스코드는 hillstate-doorlock 브랜치로 커밋했다

https://github.com/YOGYUI/HomeNetwork/tree/hillstate-doorlock

 

GitHub - YOGYUI/HomeNetwork: HomeNetwork(Homebridge) Repo

HomeNetwork(Homebridge) Repo. Contribute to YOGYUI/HomeNetwork development by creating an account on GitHub.

github.com

 

4. 동작 테스트

연동기 하드웨어의 신호선을 다이렉트로 건드린 거라 월패드를 통해서는 안내 메시지가 송출되지 않고, 도어락에서만 열림에 대한 안내 메시지를 들을 수 있다 (동영상 소리에서 도어락 안내 음성 확인 가능)

 

비록 도어락의 현재 상태값에 대해서는 알 수 없지만, 열림 명령 이후 5초간은 열림 상태를 유지할 수 있도록 Doorlock 클래스의 쓰레드를 구현했기 때문에 단순히 열고 닫는 동작에 있어서는 나름 현실과 괴리감이 크지 않게 사용할 수 있다

(지문인식이라는 편한 기능이 있기 때문에 굳이 아이폰이나 애플워치로 문을 여는 상황은 많이 발생하진 않을듯 ㅎㅎ)

5. Home Assistant 액세서리 추가

Home Assistant에서는 'lock' 아이템으로 하나 추가해줬다

lock:
  - platform: mqtt
    name: "현관 도어락"
    unique_id: "doorlock_frontdoor"
    state_topic: "home/hillstate/doorlock/state"
    command_topic: "home/hillstate/doorlock/command"
    value_template: '{{ value_json.state }}'
    payload_lock: '{ "state": "Secured" }'
    payload_unlock: '{ "state": "Unsecured" }'
    state_locked: "Secured"
    state_unlocked: "Unsecured"
    optimistic: false
    retain: false
    icon: mdi:door-closed-lock

lock 아이템의 문제인지, 구글 어시스턴트에는 아이템이 연동되질 않아서 구글 홈 미니 음성 명령이 먹히질 않는다.. 다른 아이템으로 교체하는걸 고려해봐야겠다 ㅠ (HA는 어려워;;)


열심히 달려왔다..

이제 현관 비디오폰 영상 연동만 하면 대충 하고자 했던 건 다 끝난다.. (물론 자동화를 통한 스마트라이프 구축은 이제부터 시작이지만 ㅠ)

[TODO]

  • 조명 On/Off
  • 아울렛(전원 콘센트) On/Off - 실시간 전력량 조회는 불가능
  • 도시가스 차단
  • 난방 제어 (On/Off, 희망 온도 설정, 현재 방 온도 가져오기)
  • 환기 (전열교환기)
  • 시스템 에어컨 (냉방 및 공기청정)
  • 엘리베이터 호출
  • 도어락 해제
  • Optional: 현관 비디오폰 영상, 거실 천장 모션 센서
반응형