일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 미국주식
- 현대통신
- 배당
- 해외주식
- Apple
- 매터
- 코스피
- 애플
- 월패드
- 국내주식
- 파이썬
- esp32
- 티스토리챌린지
- 홈네트워크
- Bestin
- raspberry pi
- Python
- Home Assistant
- homebridge
- 공모주
- 오블완
- Espressif
- RS-485
- 힐스테이트 광교산
- 나스닥
- MQTT
- SK텔레콤
- matter
- ConnectedHomeIP
- cluster
- Today
- Total
YOGYUI
현대통신 월패드 '디밍조명' 제어 기능 추가 (깃허브, HA 애드온) 본문
Add Hyundai HT Wallpad 'dimming light' device type
1. 개요
3주 전인 2024년 7월 11일, 현대통신 월패드 RS-485 연동 깃허브 소스코드에 처음으로 Pull-Request가 등록됐다
https://github.com/YOGYUI/HomeNetwork/pull/12
PR 주제는 현대통신 월패드의 '디밍조명' 디바이스 타입 추가 및 제어 기능 추가
※ 디밍(dimming) 조명: 밝기(brightness)를 동적으로 변경할 수 있는 조명
고급 개발자(?)답게 코드 추가/수정 및 테스트까지 완료하신 후에 PR을 올려주셨기에 별 고민없이 merge를 진행했다 (사실 우리집에는 디밍조명 타입이 없어서 테스트를 해볼 수도 없다 ㅠ)
주요변경사항은 패킷의 4번째 바이트가 0x1A이면 디밍조명과 관련된 패킷이라는 점
(참고로 0x19는 일반조명, 0x15는 감성조명)
그리고 On/Off 상태 패킷 파싱 및 On/Off 제어는 일반 조명과 동일하다는 점으로 일반조명의 파서/제어 코드를 그대로 썼다는 점이다
흥미로운 점은 작성자분의 거주 환경에서는 월패드에 디밍조명 타입으로 디바이스가 등록되었음에도 불구하고 월패드 및 스마트폰 앱에서 밝기 제어 인터페이스가 제공되지 않고 있다고 한다 (흠터레스팅...)
2. 패킷 상세 (밝기 제어)
실컷 디밍조명 타입으로 설정했는데도 불구하고 밝기 조절이 안되면 일반 조명과 다를 바가 없기에 웹을 조금 뒤져서 디밍조명의 밝기 변경과 관련된 패킷 상세를 허술하게나마 알아낼 수 있었다 (직접 테스트할 수 있는 장치가 없으니 많이 답답하다 ㅠ)
패킷 상세에 잘못된 부분이 있다면 댓글로 알려주시기 바랍니다~
2.1. 조명 전원 On/Off
2.1.1. 상태 쿼리
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 01 | 40 | XX | 00 | 00 | YY | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '상태 쿼리' = 0x01 (통신방향: 월패드→디바이스)
- 6번째 바이트: 명령/상태 타입 = On/Off = 0x40
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
예시) 2번째 방의 3번째 장치 = 0x23 - 10번째 바이트: XOR Checksum
2.1.2. 상태 응답
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 04 | 40 | XX | -- | YY | ZZ | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '상태 응답' = 0x04 (통신방향: 월패드←디바이스)
- 6번째 바이트: 명령/상태 타입 = On/Off = 0x40
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
- 9번째 바이트: 현재 조명 On/Off 상태, 0x02=OFF, 0x01=ON (일반 조명과 동일)
- 10번째 바이트: XOR Checksum
2.1.3. 명령
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 02 | 40 | XX | YY | 00 | ZZ | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '명령' = 0x02 (통신방향: 월패드→디바이스)
- 6번째 바이트: 명령/상태 타입 = On/Off = 0x40
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
- 8번째 바이트: 현재 조명 On/Off 상태, 0x02=OFF, 0x01=ON (일반 조명과 동일)
- 10번째 바이트: XOR Checksum
2.2. 조명 밝기
2.2.1. 상태 쿼리
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 01 | 42 | XX | 00 | 00 | YY | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '상태 쿼리' = 0x01 (통신방향: 월패드→디바이스)
- 6번째 바이트: 명령/상태 타입 = Brightness = 0x42
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
- 10번째 바이트: XOR Checksum
2.2.2. 상태 응답
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 04 | 42 | XX | -- | YY | ZZ | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '상태 응답' = 0x04 (통신방향: 월패드←디바이스)
- 6번째 바이트: 명령/상태 타입 = Brightness = 0x42
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
- 9번째 바이트: 현재 조명 밝기값
- 10번째 바이트: XOR Checksum
2.2.3. 명령
Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Value | F7 | 0B | 01 | 1A | 02 | 42 | XX | YY | 00 | ZZ | EE |
- 패킷 길이 11
- 4번째 바이트: 디바이스 타입 = 0x1A
- 5번째 바이트: 패킷 타입 = '명령' = 0x02 (통신방향: 월패드→디바이스)
- 6번째 바이트: 명령/상태 타입 = Brightness = 0x42
- 7번째 바이트: 상위 4비트 = 공간 인덱스, 하위 4비트 = 장치 인덱스 (1-based)
- 8번째 바이트: 설정하고자 하는 조명 밝기값
- 10번째 바이트: XOR Checksum
3. 코드 수정
3.1. DimmingLight 클래스 수정
class DimmingLight(Device):
brightness: int = 0 # 현재 밝기 레벨
brightness_prev: int = 0 # 현재 밝기 레벨 버퍼
max_brightness_level: int = 7
def publishMQTT(self):
brightness_conv = self.convert_word_to_level(self.brightness)
obj = {
"state": self.state,
"brightness": brightness_conv
}
if self.mqtt_client is not None:
self.mqtt_client.publish(self.mqtt_publish_topic, json.dumps(obj), 1)
def setMaxBrightnessLevel(self, level: int):
self.max_brightness_level = level
writeLog(f"{str(self)} Set Max Brightness Level: {self.max_brightness_level}", self)
def updateState(self, state: int, **kwargs):
self.state = state
if not self.init:
self.publishMQTT()
self.init = True
if self.state != self.state_prev:
self.publishMQTT()
self.state_prev = self.state
# 밝기 레벨
brightness = kwargs.get('brightness')
if brightness is not None:
self.brightness = brightness
if self.brightness != self.brightness_prev:
self.publishMQTT()
self.brightness_prev = self.brightness
DimmingLight 클래스에 밝기(brightness) 관련 멤버변수를 추가해줬다
또한, 디밍조명 디바이스 타입이 우리집 월패드에는 없기 때문에 월패드 상의 최대 밝기 레벨을 하드코딩할 수가 없어 max_brightness_level 멤버변수를 추가한 뒤 이를 config.xml로 수정할 수 있게 구현했다
3.2. 쿼리/명령 패킷 생성 구문 추가
class DimmingLight(Device):
def makePacketQueryState(self) -> bytearray:
packet = bytearray([0xF7, 0x0B, 0x01, 0x1A, 0x01, 0x40])
packet.append((self.room_index << 4) + (self.index + 1))
packet.extend([0x00, 0x00])
packet.append(self.calcXORChecksum(packet))
packet.append(0xEE)
return packet
def makePacketQueryBrightness(self) -> bytearray:
packet = bytearray([0xF7, 0x0B, 0x01, 0x1A, 0x01, 0x42])
packet.append((self.room_index << 4) + (self.index + 1))
packet.extend([0x00, 0x00])
packet.append(self.calcXORChecksum(packet))
packet.append(0xEE)
return packet
def makePacketSetState(self, state: bool) -> bytearray:
packet = bytearray([0xF7, 0x0B, 0x01, 0x1A, 0x02, 0x40])
packet.append((self.room_index << 4) + (self.index + 1))
if state:
packet.extend([0x01, 0x00])
else:
packet.extend([0x02, 0x00])
packet.append(self.calcXORChecksum(packet))
packet.append(0xEE)
return packet
def makePacketSetBrightness(self, brightness: int) -> bytearray:
packet = bytearray([0xF7, 0x0B, 0x01, 0x1A, 0x02, 0x42])
packet.append((self.room_index << 4) + (self.index + 1))
packet.extend([max(0, min(brightness, self.max_brightness_level)), 0x00])
packet.append(self.calcXORChecksum(packet))
packet.append(0xEE)
return packet
패킷 생성 구문은 앞서 작성한 패킷명세에 맞춰 수정~
3.3. 파싱 구문 수정
class PacketParser:
def interpretPacket(self, packet: bytearray):
# 생략
if packet[3] == 0x1A: # 디밍조명
self.handleDimmingLight(packet)
packet_info['device'] = 'dimming light'
# 생략
def handleDimmingLight(self, packet: bytearray):
room_idx = packet[6] >> 4
if packet[4] == 0x04:
state_type = packet[5]
dev_idx = packet[6] & 0x0F
if state_type == 0x40:
state = 0 if packet[8] == 0x02 else 1
result = {
'device': DeviceType.DIMMINGLIGHT,
'index': dev_idx - 1,
'room_index': room_idx,
'state': state,
'brightness': None
}
self.updateDeviceState(result)
elif state_type == 0x42:
brightness = packet[8]
result = {
'device': DeviceType.DIMMINGLIGHT,
'index': dev_idx - 1,
'room_index': room_idx,
'state': None,
'brightness': brightness
}
self.updateDeviceState(result)
상태 패킷 쿼리 구문은 On/Off 상태와 Brightness 상태 패킷을 별도로 다룰 수 있게 if-else 조건문으로 구현~
3.4. Home Assistant Discovery용 json 템플릿 수정
Home Assistant의 MQTT Light 관련 템플릿은 공식 문서를 참고해 3번의 삽질 끝에 구현 성공!
https://www.home-assistant.io/integrations/light.mqtt/
class DimmingLight(Device):
def configMQTT(self, retain: bool = False):
topic = f'{self.ha_discovery_prefix}/light/{self.unique_id}/config'
obj = {
"name": self.name,
"object_id": self.unique_id,
"unique_id": self.unique_id,
"state_topic": self.mqtt_publish_topic,
"command_topic": self.mqtt_subscribe_topic,
"schema": "template",
"command_on_template": '{'\
'"state": 1'\
'{%- if brightness is defined -%}'\
', "brightness": {{ brightness }}'\
'{%- endif -%}'\
'}',
"command_off_template": '{"state": 0}',
"state_template": "{% if value_json.state %} on {% else %} off {% endif %}",
"brightness_template": '{{ value_json.brightness }}'
}
self.mqtt_client.publish(topic, json.dumps(obj), 1, retain)
테스트삼아 위 템플릿대로 HA configuration.yaml 파일을 수정한 뒤 설정 파일을 reload해보니
HA에서 전원과 밝기 제어가 가능한 액세서리가 추가되는 것을 확인!
당연하게도 실제 장치 테스트는 진행하지 못하고 단순하게 unit test로 상태 반영 및 명령 패킷 전송에 대해서만 검증 완료 ㅠ
3.5. GitHub 소스코드 커밋
PR merge 후 6번의 커밋을 추가로 진행했다
디밍조명이 추가된 소스코드는 main 브랜치에 모두 적용해뒀다
https://github.com/YOGYUI/HomeNetwork/tree/main/Hillstate-Gwanggyosan
3.6. 간접 테스트(?) 결과
PR 올려주신 개발자분께 과감히 실제 장치 테스트를 의뢰했다!
다행히도 밝기 제어 관련 패킷이 의도했던 대로 동작하는 것 같다
원래는 월패드의 최대 밝기 레벨을 10으로 커밋했었는데, 다양한 환경에서 테스트하지 못했기에 그냥 디폴트값을 7로 다시 커밋했다 ^^;;
월패드/스마트폰 앱에서 제공하지 않는 디밍 조명 밝기 제어 인터페이스를 뚫었(??)다는데 의의가 있다고 볼 수 있겠다 ㅋㅋㅋ
[24.07.31] 수정
월패드에서는 디밍 제어 인터페이스가 제공된다고 한다 ㅎㅎ
4. HA 애드온 버전 업데이트
HA 애드온도 디밍 조명 사용 시 discovery 후 제어가 가능하도록 버전을 1.1.2로 업데이트했다
https://github.com/YOGYUI/homeassistant-addons/tree/main/homenet-hillstate
애드온 기타 설정(etc)에 dimminglight 관련 최대 밝기 및 소수점 처리 방법에 대한 파라미터를 추가해뒀다
5. 마무리
디밍조명 디바이스 타입이 있는 유저라면 사용하면서 발생하는 문제점이나 개선사항을 블로그 댓글이나 방명록 혹은 이메일 (lee2002w@gmail.com)으로 알려주시기 바랍니다~
※ 실제 장치가 없어서 문제 파악이 힘든 상황
'홈네트워크(IoT) > 힐스테이트 광교산' 카테고리의 다른 글
현대통신 월패드 HA 애드온 RS-485 명령 반복 전송 파라미터 추가 (0) | 2024.08.22 |
---|---|
현대통신 월패드 시스템에어컨 '운전모드' 제어 기능 추가 (깃허브) (0) | 2024.08.02 |
현대통신 월패드 HA 애드온 주방 비디오폰 설정 기능 추가 (5) | 2024.06.10 |
현대통신 월패드 '감성조명' 제어 기능 추가 (HA 애드온) (0) | 2024.06.07 |
현대통신 월패드 RS-485 상태 조회 패킷 주기적 전송 기능 추가 (깃허브, HA 애드온) (4) | 2024.03.28 |