YOGYUI

힐스테이트 광교산::주방 비디오폰 연동 - 세대 및 공동 현관문 제어 (애플 홈킷) 본문

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

힐스테이트 광교산::주방 비디오폰 연동 - 세대 및 공동 현관문 제어 (애플 홈킷)

요겨 2022. 10. 28. 00:10
반응형

주방에 설치되어 있는 (주)동영엠텍의 DM-D5102QMS 주방용 TV폰의 RS-485 패킷 해석 및 응용 관련 글을 2개 올린 바 있다 

힐스테이트 광교산::주방 비디오폰 RS-485 패킷 해석

 

힐스테이트 광교산::주방 비디오폰 RS-485 패킷 해석

지난주 금요일(09/02) 밤에 이메일을 한통 받았다 제목만으로도 가슴을 설레게 하는(?) 그런 메일 ㅋㅋㅋ 너무나도 공손하게 보내셔서 끝까지 정독할 수 밖에 없는 메일이었다 ^^ 요점은 월패드와

yogyui.tistory.com

힐스테이트 광교산::주방 비디오폰 연동 - HEMS(에너지 모니터링)

 

힐스테이트 광교산::주방 비디오폰 연동 - HEMS(에너지 모니터링)

지난 9월 초 주방에 설치된 (주)동영엠텍의 DM-D5102QMS 주방용 TV폰의 RS-485 패킷 해석 의뢰를 맡아 진행한 바 있다 힐스테이트 광교산::주방 비디오폰 RS-485 패킷 해석 힐스테이트 광교산::주방 비디

yogyui.tistory.com

주방 비디오폰은 HEMS (에너지 모니터링)보다는 세대 현관문 및 공동 현관문의 호출 시 영상 및 음성 통화 + 문열림 기능이 주된 편의기능이다

세대 현관문 비디오 영상/음성 통화 및 도어락 잠금 해제

세대/공동 현관문과 주방 비디오폰간의 물리적 거리 및 호출 시 다른 세대(옆집)에 초인종 소리로 인한 소음 문제 등 패킷 캡쳐 실험을 하는데 제약사항이 많아서 텀을 길게 두고 코드를 조금씩~조금씩 구현해왔다

(거의 2달정도 걸린듯 ㅎㅎ)

 

아직 모든 기능이 완벽하게 연동된 건 아니지만, 지금까지 작업한 내용을 기록으로 남길 겸 포스팅해본다

1. Hardware

UTP 케이블의 핀맵은 지난번 HEMS에서 언급한 바와 마찬가지다

https://honhon20.tistory.com/92

  • 1번 (+흰): GND
  • 2번 (): SVS > Composite Video 시그널
  • 3번 (+흰): LS2 > 음성 신호로 추정
  • 4번 (): SC2B > RS-485 통신선(B)
  • 5번 (+흰): SC2A > RS-485 통신선(A)
  • 6번 (): LS1 > 음성 신호로 추정
  • 7번 (+흰): GND
  • 8번 (): SUB12 > 현관 도어폰 전원 (+12V, 주방 비디오폰에서는 의미없음)

결선도는 다음과 같다

너저분...

Homebridge 서버로 쓰던 SBC(Raspberry Pi)를 주방 전자레인지 위에 올려두고 구현 및 테스트를 진행했다

주방이 너무 지저분해졌는데, 추후 주방 비디오폰 연동용 임베디드 시스템을 따로 구현할 생각!

2. State Machine Design

주방 비디오폰 현관문 제어 State Machine Diagram

세대 현관문과 공동 현관문을 호출하고, 통화하거나 혹은 문열림 버튼을 누르면서 주방 비디오폰의 제어 상태를 위와 같이 간단히 그려봤다

 

각 상태별 간단한 설명은 다음과 같다

  • IDLE: 휴지(休止) 상태
  • FRONT DOOR RINGING: 세대 현관문 초인종이 울리고 있는 상태
  • COMMUNAL DOOR RINGING: 공동 현관문 초인종이 울리고 있는 상태
  • A/V STREAMING: 세대 현관문 혹은 공동 현관문과 통화중 (영상 및 음성 송출)인 상태
  • UNLOCK FRONT/COMMUNAL DOOR: 세대 현관문 혹은 공동 현관문 잠금 해제 상태

개인적인 실험으로는 세대 현관문과 공동 현관문이 동시에 호출되는 경우를 혼자서는 물리적으로 만들 수가 없어서 상태를 분리해뒀는데, 실제 구동 환경은 어떨런지 잘 모르겠다 ㅋㅋ

 

※ 세대 현관문은 초인종 호출 없이도 집안 내부에서 주방 비디오폰으로 통화중 상태로 진입한 뒤 문열림 상태로 진입이 가능한 것이 특징인데, 이를 통해 스마트폰으로 현관 도어락을 해제할 수 있다

- 일전에 I/O 방식으로 도어락 해제 기능을 구현했는데, 좀 더 스마트해질 수 있다!

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

 

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

거실 월패드에서는 비디오폰을 통화를 하거나, 현관 출입문 도어락을 해제할 수 있다 (문열기) 문열기 버튼을 클릭했을 때, 조명과 아울렛이 연결된 RS-485 포트에서 처음보는 패킷을 캡쳐할 수

yogyui.tistory.com

3. RS-485 패킷 명세

주방 비디오폰과 거실 월패드가 주고받는 패킷 중 세대 현관문/공동 현관문 제어와 관련된 녀석들을 추출해서 위에서 그린 State Machine Diagram과 매칭시켜보자

 

패킷 포맷: 7F XY 00 00 EE

요약: 2번째 바이트의 상위 4비트(X) 값이 0xB이면 세대현관문, 0x5 혹은 0x6이면 공동현관문 관련 패킷

패킷 통신 방향 상태 전이
7F B4 00 00 EE 비디오폰 → 월패드 FRONT DOOR RINGING → UNLOCK FRONT/COMMUNAL DOOR
7F B5 00 00 EE 월패드 → 비디오폰 IDLEFRONT DOOR RINGING
7F B6 00 00 EE 월패드 → 비디오폰 FRONT DOOR RINGING → IDLE
7F B7 00 00 EE 비디오폰 → 월패드 FRONT DOOR RINGINGA/V STREAMING
7F B8 00 00 EE 비디오폰 → 월패드 FRONT DOOR RINGING → IDLE
7F B9 00 00 EE 비디오폰 → 월패드 IDLE → A/V STREAMING
7F BA 00 00 EE 비디오폰 → 월패드 A/V STREAMINGIDLE 
7F 5A 00 00 EE 월패드 → 비디오폰 IDLE → COMMUNAL DOOR RINGING
7F 5C 00 00 EE 월패드 → 비디오폰 COMMUNALDOOR RINGINGIDLE
7F 5E 00 00 EE 월패드 → 비디오폰 A/V STREAMING → IDLE
7F 5F 00 00 EE 비디오폰 → 월패드 COMMUNALDOOR RINGINGA/V STREAMING
7F 60 00 00 EE 비디오폰 → 월패드 A/V STREAMING → IDLE
7F 61 00 00 EE 비디오폰 → 월패드 A/V STREAMING → UNLOCK FRONT/COMMUNAL DOOR

※ 반복 실험의 한계(소음;;) 때문에 정확하지 않을 가능성이 크다 ^^;;

 

특히 세대 현관문 쪽 상태 전이는 헷갈리는 점이 많았는데, 네이버 카페의 글을 많이 참고했다 (회원 가입 필요..)

 

HomeAssistant : 네이버 카페

스마트홈에 관심이 많은 사람들의 모임. 셀프인테리어, 스마트홈 인테리어, IOT. 홈iot, 홈diy, 홈네트워크

cafe.naver.com

4. Python 코드 구현

호출된 상태에서 영상을 수신하는 통화중 상태로 갈지, 그냥 호출을 종료시킬 지, 문열림 상태로 갈지 등 3가지 분기로 진행할 수 있으니, 각 상황에 맞게 코드를 조건문을 활용해서 구현해줘야 한다

(특히 세대 현관문 제어는 도어락 제어랑 현동시킬 수 있다)

 

우선 '호출' 및 '도어락 잠금 상태'에 대한 열거형 클래스를 선언해주자

from enum import IntEnum

class StateRinging(IntEnum):
    # 서브폰의 호출 상태
    IDLE = 0
    FRONT = 1  # 현관문 초인종
    COMMUNAL = 2  # 공동출입문

class StateDoorLock(IntEnum):
    Unsecured = 0
    Secured = 1
    Jammed = 2
    Unknown = 3

4.1. 주방 비디오폰 디바이스 클래스

주방 비디오폰 클래스 SubPhone은 내부 멤버변수로 

state_streaming(통화중) / state_ringing(초인종 호출중) / state_doorlock(문열림)

3가지 상태값을 별개로 나누어 관리하게 구현했다 (MQTT 액세서리별로 publish할 때 이게 좀 더 코드가 깔끔하더라...)

class SubPhone(Device):
    state_streaming: int = 0
    state_ringing: StateRinging = StateRinging.IDLE
    state_doorlock: StateDoorLock = StateDoorLock.Secured
    
    def __init__(self, name: str = 'SubPhone', **kwargs):
        super().__init__(name, **kwargs)
        # for ffmpeg configuration
        self.streaming_config = {
            'conf_file_path': '',
            'feed_path': '',
            'input_device': '/dev/video0',
            'frame_rate': 24,
            'width': 320,
            'height': 240,
        }
    
    def publish_mqtt(self):
        topic = self.mqtt_publish_topic
        if self.mqtt_client is not None:
            obj = {"state": self.state_streaming}
            self.mqtt_client.publish(topic + '/streaming', json.dumps(obj), 1)

            if self.state_ringing in [StateRinging.FRONT, StateRinging.COMMUNAL]:
                self.mqtt_client.publish(topic + '/doorbell', 'ON', 1)
            else:
                self.mqtt_client.publish(topic + '/doorbell', 'OFF', 1)

            obj = {"state": self.state_doorlock.name}  # 도어락은 상태 조회가 안되고 '열기' 기능만 존재한다
            self.mqtt_client.publish(topic + '/doorlock', json.dumps(obj), 1)
     
     def updateState(self, state: int, **kwargs):
        streaming = kwargs.get('streaming')
        if streaming is not None:
            self.state_streaming = streaming
            self.sig_state_streaming.emit(self.state_streaming)
            self.publish_mqtt()
        ringing_front = kwargs.get('ringing_front')
        if ringing_front is not None:
            if ringing_front:
                self.state_ringing = StateRinging.FRONT
            else:
                self.state_ringing = StateRinging.IDLE
            self.publish_mqtt()
        ringing_communal = kwargs.get('ringing_communal')
        if ringing_communal is not None:
            if ringing_communal:
                self.state_ringing = StateRinging.COMMUNAL
            else:
                self.state_ringing = StateRinging.IDLE
            self.publish_mqtt()
        doorlock = kwargs.get('doorlock')
        if doorlock is not None:
            self.state_doorlock = StateDoorLock(doorlock)
            self.publish_mqtt()

    def makePacketCommon(self, header: int) -> bytearray:
        return bytearray([0x7F, max(0, min(0xFF, header)), 0x00, 0x00, 0xEE])

    def makePacketSetVideoStreamingState(self, state: int) -> bytearray:
        if state:
            if self.state_ringing == StateRinging.FRONT:
                # 현관 초인종 카메라 영상 서브폰 우회
                return self.makePacketCommon(0xB7)
            elif self.state_ringing == StateRinging.COMMUNAL:
                # 공동현관문 영상 우회
                return self.makePacketCommon(0x5F)
            else:
                # 단순 문열기용 (주방 서브폰 활성화)
                return self.makePacketCommon(0xB9)
        else:
            if self.state_ringing == StateRinging.FRONT:
                # 현관 초인종 카메라 영상 서브폰 우회 종료
                return self.makePacketCommon(0xB8)
            elif self.state_ringing == StateRinging.COMMUNAL:
                # 공동현관문 영상 우회 종료
                return self.makePacketCommon(0x60)
            else:
                # 단순 문열기용 (주방 서브폰 활성화)
                return self.makePacketCommon(0xBA)

    def makePacketOpenFrontDoor(self) -> bytearray:
        # 현관 초인종 카메라 영상이 서브폰으로 우회된 상태에서 도어락 해제
        return self.makePacketCommon(0xB4)

    def makePacketOpenCommunalDoor(self) -> bytearray:
        # 공동현관문 호출 후 카메라 영상이 우회된 상태에서 열림 명령
        return self.makePacketCommon(0x61)

4.2. RS-485 패킷 파서 클래스

파서 클래스의 패킷 해석 구문은 위에서 설계한 State Machine 및 SubPhone 클래스의 상태 변수 값을 바꾸기 위해 적절히 dict key를 설정해서 콜백하게 만들어줬다

class ParserSubPhone(PacketParser):
    def handlePacket(self):
        # 3840 baudrate
        # packet format: 7F XX XX XX EE
        idx = self.buffer.find(0x7F)
        if idx > 0:
            self.buffer = self.buffer[idx:]
        if len(self.buffer) >= 2:
            idx2 = self.buffer.find(0xEE)
            if idx2 > 0:
                self.line_busy = False
                packet = self.buffer[:idx2 + 1]
                self.interpretPacket(packet)
                self.buffer = self.buffer[idx2 + 1:]

    def interpretPacket(self, packet: bytearray):
        if (packet[1] & 0xF0) == 0xB0:  # 현관 도어폰 호출
            self.handleFrontDoor(packet)
        elif (packet[1] & 0xF0) == 0x50:  # 공동 현관문 호출
            self.handleCommunalDoor(packet)
    
    def handleFrontDoor(self, packet: bytearray):
        result = {'device': 'subphone'}
        notify: bool = True
        if packet[1] == 0xB5:
            # 현관 도어폰 초인종 호출 (월패드 -> 서브폰)
            result['ringing_front'] = 1
        elif packet[1] == 0xB6:
            # 현관 도어폰 초인종 호출 종료 (월패드 -> 서브폰)
            result['ringing_front'] = 0
        elif packet[1] == 0xB9:
            # 서브폰에서 현관 통화 시작 (서브폰 -> 월패드)
            result['streaming'] = 1
        elif packet[1] == 0xBA:
            # 서브폰에서 현관 통화 종료 (서브폰 -> 월패드)
            result['streaming'] = 0
        elif packet[1] == 0xB4:
            # 서브폰에서 현관문 열림 명령 (서브폰 -> 월패드)
            result['doorlock'] = 0  # Unsecured
        elif packet[1] in [0xBB, 0xB8]:
            # 현관 도어폰 통화 종료
            result['ringing_front'] = 0
            result['streaming'] = 0
            result['doorlock'] = 1  # Secured
        else:
            notify = False
        if notify:
            self.sig_parse_result.emit(result)

    def handleCommunalDoor(self, packet: bytearray):
        result = {'device': 'subphone'}
        notify: bool = True
        if packet[1] == 0x5A:
            # 공동현관문 호출 (월패드 -> 서브폰)
            result['ringing_communal'] = 1
        elif packet[1] == 0x5C:
            # 공동현관문 호출 종료 (월패드 -> 서브폰)
            result['ringing_communal'] = 0
        elif packet[1] == 0x5E:
            # 공동현관문 통화 종료
            result['ringing_communal'] = 0
            result['streaming'] = 0
            result['doorlock'] = 1  # Secured
        else:
            notify = False
        if notify:
            self.sig_parse_result.emit(result)

4.3. FFMpeg 및 FFServer 런타임 구동

FFMpeg와 FFServer를 외부에서 구동하려고 하니 라즈베리파이 재부팅 시 자동 실행 옵션도 넣어야하고, 구동되고 있는지 여부를 별도로 쿼리해야하는 등 귀찮은 점들이 있어서 홈네트워크 앱이 실행될 때 같이 실행되도록 코드를 변경했다

단순히 subprocess를 활용해서 메인 이벤트 루프에서 호출했더니 이벤트 루프 전체를 잡아먹길래, FFMpeg와 FFServer를 각각 다른 프로세스에서 돌릴 수 있게 multiprocessing을 활용해서 개별 프로세스에서 subprocess가 구동될 수 있게 꾸며줬다

  • 여러개의 서브프로세스를 멀티프로세싱할 수 있는 약간의 고급 구현법
    (멀티프로세싱에 익숙하지 않은 개발자라면 어질어질할 수 있지만, 최대한 가독성을 살려서 구현해봤다 ㅎㅎ...)
  • 코드 내부 서브프로세스 호출 명령어가 모두 "exec"로 시작한다
  • 프로세스 정상 종료 위해 메인 이벤트 루프로 Pipe를 활용해서 각 프로세스별 PID 값을 넘겨준다
import subprocess
import multiprocessing as mp
from multiprocessing import connection

def procFFMpeg(cfg: dict, pipe: connection.Connection):
    pid = os.getpid()
    name = mp.current_process().name

    idev = cfg.get('input_device')
    fps = cfg.get('frame_rate')
    width = cfg.get('width')
    height = cfg.get('height')
    feed = cfg.get('feed_path')
    cmd = f"exec ~/ffmpeg/ffmpeg -i {idev} -r {fps} -s {width}x{height} -threads 1 {feed}"

    with subprocess.Popen(cmd, 
        shell=True, 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.STDOUT
    ) as subproc:
        pid = subproc.pid
        msg = f'{pid}'
        pipe.send_bytes(msg.encode(encoding='utf-8', errors='ignore'))
        buff = subproc.stdout.read() 
        print(buff.decode(encoding='UTF-8'))

def procFFServer(cfg: dict, pipe: connection.Connection):
    pid = os.getpid()
    name = mp.current_process().name
    prefix = f'[MultiProcess][{name}({pid})] '

    conf_path = cfg.get('conf_file_path')
    cmd = f'exec ~/ffmpeg/ffserver -f {conf_path}'
    
    with subprocess.Popen(cmd, 
        shell=True, 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.STDOUT
    ) as subproc:
        pid = subproc.pid
        msg = f'{pid}'
        pipe.send_bytes(msg.encode(encoding='utf-8', errors='ignore'))
        buff = subproc.stdout.read() 
        print(buff.decode(encoding='UTF-8'))
import psutil
import multiprocessing

class Home:
    mp_ffserver: Union[multiprocessing.Process, None] = None
    pid_ffserver_proc: int = 0
    mp_ffmpeg: Union[multiprocessing.Process, None] = None
    pid_ffmpeg_proc: int = 0
    
    def initialize(self, init_service: bool, connect_rs485: bool):
        if self.rs485_subphone_config.enable:
            self.startFFServer()
            self.startFFMpeg()
    
    def release(self):
        if self.rs485_subphone_config.enable:
            self.stopFFMpeg()
            self.stopFFServer()
    
    def startFFServer(self):
        pipe1, pipe2 = multiprocessing.Pipe(duplex=True)
        args = [self.subphone.streaming_config, pipe1]
        self.mp_ffserver = multiprocessing.Process(target=procFFServer, name='FFServer', args=tuple(args))
        self.mp_ffserver.start()
        while True:
            if pipe2.poll():
                recv = pipe2.recv_bytes().decode(encoding='utf-8', errors='ignore')
                writeLog(f'Recv from FFServer process pipe: {recv}', self)
                self.pid_ffserver_proc = int(recv)
                break
    
    def stopFFServer(self):
        if self.mp_ffserver is not None:
            psutil.Process(self.pid_ffserver_proc).kill()
            self.mp_ffserver.terminate()
            writeLog(f'FFServer Process Terminated', self)
        self.mp_ffserver = None
        
    def startFFMpeg(self):
        pipe1, pipe2 = multiprocessing.Pipe(duplex=True)
        args = [self.subphone.streaming_config, pipe1]
        self.mp_ffmpeg = multiprocessing.Process(target=procFFMpeg, name='FFMpeg', args=tuple(args))
        self.mp_ffmpeg.start()
        while True:
            if pipe2.poll():
                recv = pipe2.recv_bytes().decode(encoding='utf-8', errors='ignore')
                writeLog(f'Recv from FFMpeg process pipe: {recv}', self)
                self.pid_ffmpeg_proc = int(recv)
                break
        proc = psutil.Process(self.pid_ffmpeg_proc)
        proc.nice(0)
        
    def stopFFMpeg(self):
        if self.mp_ffmpeg is not None:
            psutil.Process(self.pid_ffmpeg_proc).kill()
            self.mp_ffmpeg.terminate()
            writeLog(f'FFMpeg Process Terminated', self)
        self.mp_ffmpeg = None

전체 코드는 언제나처럼 깃허브 저장소에 커밋해뒀다

https://github.com/YOGYUI/HomeNetwork/tree/main/Hillstate-Gwanggyosan

 

GitHub - YOGYUI/HomeNetwork: HomeNetwork(Homebridge) Repo

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

github.com

5. Homebridge 액세서리 추가

주방 비디오폰의 영상 수신을 위해 Camera-FFMpeg 플러그인을 활용했다

https://github.com/Sunoo/homebridge-camera-ffmpeg#readme

 

GitHub - Sunoo/homebridge-camera-ffmpeg: Homebridge Plugin Providing FFmpeg-based Camera Support

Homebridge Plugin Providing FFmpeg-based Camera Support - GitHub - Sunoo/homebridge-camera-ffmpeg: Homebridge Plugin Providing FFmpeg-based Camera Support

github.com

{
    "platform": "Camera-ffmpeg",
    "mqtt": "mqtt broker host address",
    "portmqtt": 1883,
    "usermqtt": "mqtt broker auth id",
    "passmqtt": "mqtt broker auth password",
    "cameras": [
        {
            "name": "doorphone video",
            "manufacturer": "Hyundai HT",
            "model": "HDC-100S",
            "motion": false,
            "doorbell": true,
            "switches": false,
            "unbridge": false,
            "videoConfig": {
                "source": "-i {ffserver_streaming_address}",
                "maxStreams": 2,
                "maxWidth": 640,
                "maxHeight": 480,
                "maxFPS": 60,
                "audio": false
            },
            "mqtt": {
                "doorbellTopic": "home/hillstate/subphone/state/doorbell",
                "doorbellMessage": "ON"
            }
        }
    ]
}

 

또한, 통화중 상태를 알아보기 위한 스위치 및 문열림 기능 연동을 위해 MQTT-Thing 플러그인의 'switch'와 'lockMechanism' 항목을 추가해줬다

https://github.com/arachnetech/homebridge-mqttthing#readme

 

GitHub - arachnetech/homebridge-mqttthing: A plugin for Homebridge allowing the integration of many different accessory types us

A plugin for Homebridge allowing the integration of many different accessory types using MQTT. - GitHub - arachnetech/homebridge-mqttthing: A plugin for Homebridge allowing the integration of many ...

github.com

{
    "accessory": "mqttthing",
    "type": "switch",
    "name": "SubPhone Door Camera",
    "url": "mqtt broker host address",
    "username": "mqtt broker auth id",
    "password": "mqtt broker auth password",
    "topics": {
        "getOn": {
            "topic": "home/hillstate/subphone/state/streaming",
            "apply": "return (JSON.parse(message).state == 1);"
        },
        "setOn": {
            "topic": "home/hillstate/subphone/command/streaming",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "integerValue": true,
    "onValue": 1,
    "offValue": 0,
    "logMqtt": false
},
{
    "accessory": "mqttthing",
    "type": "lockMechanism",
    "name": "SubPhone DoorLock",
    "url": "mqtt broker host address",
    "username": "mqtt broker auth id",
    "password": "mqtt broker auth password",
    "topics": {
        "getLockCurrentState": {
            "topic": "home/hillstate/subphone/state/doorlock",
            "apply": "return JSON.parse(message).state;"
        },
        "getLockTargetState": {
            "topic": "home/hillstate/subphone/state/doorlock",
            "apply": "return JSON.parse(message).state;"
        },
        "setLockTargetState": {
            "topic": "home/hillstate/subphone/command/doorlock",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "lockValues": [
        "Unsecured",
        "Secured",
        "Jammed",
        "Unknown"
    ],
    "logMqtt": false
}

6. DEMO

필수 기능들을 연동한 결과물을 영상으로 확인해보자

6.1. 세대현관문 통화 (카메라 연동)

주방 비디오폰에서 현관문 통화 모드 진입

상태 전이 흐름: IDLE  A/V STREAMING

애플 기기에서 현관문 통화 모드 진입

상태 전이 흐름: IDLE  A/V STREAMING

※ 주방 비디오폰이 도어폰과 통화중일 때는 거실 월패드에 '서브폰과 도어폰 통화 중입니다'라는 메시지가 팝업되는 것을 알 수 있다

6.2. 공동현관문 통화 (카메라 연동)

지하 6층 주차장에서 세대 호출을 누른 뒤 아이폰으로 영상을 확인해봤다

상태 전이 흐름: IDLE  COMMUNAL DOOR RINGING  A/V STREAMING

물론 애플 기기로 공동현관문의 문도 열 수 있다 (1층 공동현관문에서 문열림 기능을 찍어봤다)

상태 전이 흐름: IDLE  COMMUNAL DOOR RINGING  A/V STREAMING  UNLOCK DOOR  IDLE

※ 세대현관문 도어락은 초인종 호출이 되지 않은 상태에서도 문열림이 가능하지만, 공동현관문은 초인종 호출 상태에서만 통화중 상태로 넘어간 뒤 문열림이 가능하다 

- 아무래도 공동현관문은 여러 개가 있는데다, 영상 자체가 단지 네트워크를 통해 IP 카메라 형식으로 넘어오기 때문인 것으로 추측된다

6.3. 현관문 도어락 잠금해제

아이폰으로 방문자의 얼굴을 실시간으로 확인할 수 있다

 

집안에서 애플 기기로 현관 도어락 잠금을 해제할 수 있다 (별도의 액세서리로 빠져있기 때문에 조금 불편하긴 하지만, Siri로 음성 명령이 가능하기 때문에 큰 문제는 없다 ㅎㅎ)

상태 전이 흐름: IDLE  FRONT DOOR RINGING  A/V STREAMING  UNLOCK DOOR  IDLE

 

현관문 앞에서 아이폰으로 도어락 잠금 해제도 가능하다

상태 전이 흐름: IDLEA/V STREAMING  UNLOCK DOOR  IDLE

※ 통화 상태로 잠깐 변경되면서 도어폰의 LED 조명이 잠깐 켜지는 것을 알 수 있다

도어락 지문 인식이 잘 안될때 아주 편리하다

홈킷 도어락 액세서리는 휴대폰 잠금이 해제된 상태에서만 제어가 가능하기 때문에 스마트폰 분실에 따른 집안 도난 우려는 살짝 접어둬도 되지... 않을까? ㅋㅋ

7. TODO

UTP 케이블의 3번 핀(LS2)와 6번 핀(LS1)은 음성 신호인 것 같아 오실로스코프로 찍어봤다

노란색(CH1)이 LS1, 파란색(CH2)이 LS2 신호이며, 빨간색은 Math로 (CH1 - CH2) 계산 결과이다

약 60Hz의 정현파가 각각 출력되며, 주방 비디오폰의 마이크 부분을 손으로 문질렀을 때 Math 결과를 보면 파형의 변동이 있는 것을 알 수 있었다

즉, 일반 음성 신호가 60Hz의 differential 신호로 변조(modulation)되어 송/수신되는 것이 아닌가 의심해볼 만 하다.. differential 신호를 변조/복조하려면 OP-AMP 회로가 필요할 것 같은데, 테스트용으로 쓸만한게 없어서 시도를 못하고 있다 ㅎㅎ

어차피 지금까지 해온 내용을 토대로 임베디드 솔루션을 만들어볼 계획을 하고 있으니, 음성 연동은 프로젝트 시작 즈음에 테스트해볼까 생각중이다 


이제 집에 탑재되어 있는 홈 IoT 연동 작업은 거의 끝난 것 같다

물론 단지 네트워크 통신을 후킹해서 차량 도착이나 주차 위치같은 유용한 정보도 연동이 가능하다고는 하는데, openwrt를 거의 다뤄본 적도 없을 뿐더러 거실 월패드 벽면에 공간이 협소에서 SBC 하나 놓을 공간도 부족하기 때문에 보류!

 

슬슬 마무리짓고 Summary 포스팅해야겠다..

끝~!

 

반응형
Comments