YOGYUI

힐스테이트 광교산::일괄소등 스위치 RS-485 패킷 분석 및 애플 홈 연동 본문

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

힐스테이트 광교산::일괄소등 스위치 RS-485 패킷 분석 및 애플 홈 연동

요겨 2023. 6. 4. 00:30
반응형

 

오랜만에 힐스테이트 홈 IoT 관련 글을 포스팅한다

얼마전 블로그 독자(?) 한분으로부터 현대통신 일괄소등 기능에 대해서도 구현된 예시가 보고싶다는 요청사항을 받고 주말에 시간을 내서 후딱 만들어봤다 

1. 일괄소등 기능

일괄소등 기능은 집안의 모든 전등을 일괄적으로 꺼주는 기능을 한다

중요한 건 RS-485로 연결되어 있는 각 방의 전등들 뿐만 아니라 RS-485로 구축되지 않은 화장실 전등/환기구 구나 싱크대 레일조명 등 조명과 관련된 계통의 전원을 전부 차단해 집안에 사람이 없을 때 확실한 절전을 꾀할 수 있다

※ 나는 집안에 전등이란 전등은 죄다 홈 IoT랑 연동해서 평소에 쓰지 않는 기능인지라 현대통신 홈네트워크 구축할 때 필요성을 못느껴서 따로 구현하지 않았었다

전등 IoT 연동 예시: 힐스테이트 광교산::주방 싱크대 LED 조명 IoT 연동

 

일괄소등 기능은 현관문 근처 복도의 소형 패드에 버튼으로 구성되어 있다

- 일괄소등 / 엘리베이터 호출 / 복도 천장 조명 제어 기능이 하나로 통합되어 있음

중앙제어 회사의 3048-8 모델이 매립되어 있는것으로 파악된다 (3046-Q인가...?)

중앙제어 카탈로그

 

Hi-oT 앱에서는 거실의 '다기능스위치' 항목에 '일괄스위치'로 구성되어 있다

2. RS-485 패킷 수집

일괄소등 관련 RS-485 포트는 위 사진 기준 왼쪽에서 패킷이 오고 가는 것으로 확인됐다

사진에는 '가스'라고 표시했는데, 이 포트에서는 가스밸브 뿐만 아니라 난방, 에어컨, 환기(전열교환기), 엘리베이터 등 다양한 종류의 디바이스 관련 제어/쿼리/응답을 담당하고 있다

이제껏 해왔던 것과 마찬가지로 Hi-oT 앱으로 상태를 변경하면서 유의미하게 변하는 패킷을 모아봤다

(따로 모으는 과정을 설명하지는 않고 모은 결과물만을 게시)

2.1. 쿼리 (현재 상태 요청) 패킷

F7 0E 01 2A 01 40 10 00 19 00 1B 04 85 EE (가스밸브가 열려있을 때)

F7 0E 01 2A 01 40 10 00 19 00 1B 03 82 EE (가스밸브가 닫혀있을 때)

2.2. 응답 패킷

[일괄스위치 OFF 상태] ※ 집안 조명이 정상적으로 제어되는 상태
F7 0E 01 2A 04 40 10 00 19 02 1B 04 82 EE (가스밸브가 열려있을 때)
F7 0E 01 2A 04 40 10 00 19 02 1B 03 85 EE (가스밸브가 닫혀있을 때)

[일괄스위치 ON 상태] ※ 화장실을 포함한 모든 조명이 제어가 안되는 상태
F7 0E 01 2A 04 40 10 00 19 01 1B 04 81 EE (가스밸브가 열려있을 때)
F7 0E 01 2A 04 40 10 00 19 01 1B 03 86 EE (가스밸브가 닫혀있을 때)

2.3. 명령 패킷

[일괄스위치 ON 명령]

F7 0C 01 2A 02 40 11 01 19 00 9B EE

F7 0C 01 2A 04 40 11 01 19 01 9C EE (ON 명령에 대한 응답 패킷)

 

[일괄스위치 OFF 명령]

F7 0C 01 2A 02 40 11 02 19 00 98 EE

F7 0C 01 2A 04 40 11 02 19 02 9C EE (OFF 명령에 대한 응답 패킷)

2.4. 패킷 명세

  • 다른 디바이스 패킷들과 마찬가지로 시작 바이트(prefix)는 0xF7, 종료 바이트(suffix)는 0xEE
  • 디바이스 종류는 가리키는 4번째 바이트는 0x2A
  • 5번째 바이트는 패킷의 종류를 가리킴
    0x01: 현재 상태 쿼리
    0x02: 상태 변경 명령
    0x04: 쿼리/명령에 대한 응답
  • 명령 패킷의 경우 8번째 바이트가 0x01일 경우 일괄소등 스위치 ON 명령, 0x02일 경우 OFF 명령
  • 응답 패킷의 경우 10번째 바이트가 0x01일 경우 현재 일괄소등 스위치가 ON 상태, 0x02일 경우 OFF 상태
  • 응답 패킷의 12번째 바이트는 가스밸브 Open/Close 상태를 나타냄
    (가스밸브는 별도의 패킷이 존재하므로 따로 핸들링할 필요는 없어보임)

3. 홈네트워크 코드 구현

기존 코드에 일괄소등을 'BatchOffSwitch'라는 이름의 클래스를 추가해줬다

상태 변경 제어 패킷 및 쿼리 패킷만 존재하므로 클래스 내부는 상당히 단촐하다

@unique
class DeviceType(IntEnum):
    UNKNOWN = 0
    LIGHT = auto()
    OUTLET = auto()
    THERMOSTAT = auto()
    AIRCONDITIONER = auto()
    GASVALVE = auto()
    VENTILATOR = auto()
    ELEVATOR = auto()
    SUBPHONE = auto()
    HEMS = auto()
    BATCHOFFSWITCH = auto()
import json
from Device import *

class BatchOffSwitch(Device):
    def __init__(self, name: str = 'BatchOffSW', **kwargs):
        super().__init__(name, **kwargs)
    
    def publish_mqtt(self):
        obj = {"state": self.state}
        if self.mqtt_client is not None:
            self.mqtt_client.publish(self.mqtt_publish_topic, json.dumps(obj), 1)
    
    def makePacketQueryState(self) -> bytearray:
        # F7 0E 01 2A 01 40 10 00 19 00 1B 03 82 EE
        return bytearray([0xF7, 0x0E, 0x01, 0x2A, 0x01, 0x40, 0x10, 0x00, 0x19, 0x00, 0x1B, 0x03, 0x82, 0xEE])

    def makePacketSetState(self, state: bool) -> bytearray:
        # F7 0C 01 2A 02 40 11 XX 19 00 YY EE
        # XX: 02 = OFF 01 = ON
        # YY: Checksum (XOR SUM)
        packet = bytearray([0xF7, 0x0C, 0x01, 0x2A, 0x02, 0x40, 0x11])
        if state:
            packet.extend([0x01, 0x19, 0x00])
        else:
            packet.extend([0x02, 0x19, 0x00])
        packet.append(self.calcXORChecksum(packet))
        packet.append(0xEE)
        return packet

RS-485 패킷 파서는 다음과 같이 추가해줬다

10번째 패킷의 값이 0x02면 일괄소등 스위치가 OFF 상태, 0x01이면 일괄소등 스위치가 ON 상태인 정보를 토대로 파서 구문을 짜주면 끝~

from PacketParser import *
import datetime

class ParserVarious(PacketParser):
    def interpretPacket(self, packet: bytearray):
        if packet[3] == 0x2A:  # 일괄소등 스위치
            self.handleBatchOffSwitch(packet)
    
    def handleBatchOffSwitch(self, packet: bytearray):
        if packet[4] == 0x04:
            state = 0 if packet[9] == 0x02 else 1
            result = {
                'device': DeviceType.BATCHOFFSWITCH,
                'state': state
            }
            self.sig_parse_result.emit(result)
        writeLog(f'{self.prettifyPacket(packet)}', self)

나머지 변경사항은 아래와 같이 GitHub Commit으로 확인할 수 있다

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

 

- add batchoff switch class, handler · YOGYUI/HomeNetwork@ee8d489

Show file tree Showing 9 changed files with 124 additions and 21 deletions.

github.com

4. Homebridge 액세서리 구현

다른 디바이스와 마찬가지로 mqttthing 플러그인을 이용해서 MQTT를 통해 간단하게 GET/SET status 스위치로 다음과 같이 추가해줬다

 {
    "accessory": "mqttthing",
    "type": "switch",
    "name": "Batch Off Switch",
    "url": "mosquitto url",
    "username": "mosquitto id",
    "password": "mosquitto passwd",
    "topics": {
        "getOn": {
            "topic": "home/hillstate/batchoffsw/state",
            "apply": "return JSON.parse(message).state;"
        },
        "setOn": {
            "topic": "home/hillstate/batchoffsw/command",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "integerValue": true,
    "onValue": 1,
    "offValue": 0,
    "logMqtt": false
}

 

애플 홈 앱에서는 '일괄소등'으로 이름을 변경하고 '복도' 방에 추가해줬다

5. 테스트

일괄소등 스위치 자체는 빠르게 적용되는데, 거실 복도 조명이 꺼지기까지는 시간이 좀 걸린다

복도 천장 조명은 월패드 매립 벽면 뒤에 매설된 조명 릴레이를 통해 제어되기 때문에 월패드에서 통신을 통해서만 켜고 끌 수 있어 약간의 딜레이가 있는 것으로 추측된다 (관련 글: 힐스테이트 광교산::조명 제어 RS-485 패킷 분석 (1))

화장실 전등과 같이 전원선이 일괄소등 라인에 직접 연결되어 있는 경우 즉시 꺼지는 것을 알 수 있었다

6. 정리

기능 자체가 On/Off 스위치 하나 구현하는 것과 별반 다르지 않아 쉽고 빠르게 구현할 수 있었다

(코드를 잘 모듈화해두면 기능을 효율적으로 추가할 수 있다)

여행같이 장기간 집을 비울 경우 유용하게 쓸 수 있는 기능이긴 하지만, 앞서 언급했듯 나는 모든 종류의 조명을 홈네트워크에 연동해두고 전부 끄는 자동화를 추가해뒀기 때문에 '일괄 소등'만을 사용할 일을 앞으로도 거의 없을 것 같긴 하다 ㅋㅋ 

물론 블로그 글 하나 추가하기에는 좋은 아이템이었다

끝~!

반응형
Comments