일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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
- 빅데이터분석기사
- Home Assistant
- ConnectedHomeIP
- 힐스테이트 광교산
- matter
- 월패드
- 매터
- 애플
- Python
- 배당
- 주식
- esp32
- homebridge
- 미국주식
- RS-485
- raspberry pi
- Espressif
- 나스닥
- 해외주식
- MQTT
- cluster
- 라즈베리파이
- 국내주식
- Bestin
- 공모주
- Today
- Total
YOGYUI
광교아이파크::전원콘센트 Apple 홈킷 연동 (2) 본문
전원 콘센트 관련 Homebridge 액세서리는 다른 액세서리들과 마찬가지로 Mqttting 플러그인을 사용해 구현하기로 했다
https://github.com/arachnetech/homebridge-mqttthing#readme
깃허브 페이지를 보면 'Outlet' 항목이 있다
※ 막간 영어 상식
한국에서 흔히 사용하는 '콘센트'는 일본식 엉터리 영어! 미국가서 consent 이러면 아예 의사소통이 안된다
올바르게 번역하면 'electrical outlet' 혹은 'power outlet'이라고 해야 한다 (옷사러 갈때 그 '아울렛'이다)
기존에 구현해뒀던 조명 관련 구현에 추가하는 방향으로 빠르게 구현을 해보자
(전체 구현 코드는 깃허브 링크 참고)
1. 서버 코드 (python) 수정
우선 Outlet 클래스를 추가해주자
mqtt publish 시 필요한 정보는 'On/Off' 정보 및 '현재 소모전력값'이다
class Outlet(Device):
measurement: float = 0.
measurement_prev: float = 0.
def __init__(self, name: str = 'Device', index: int = 0, **kwargs):
self.index = index
super().__init__(name, **kwargs)
def publish_mqtt(self):
obj = {
"state": self.state,
"watts": self.measurement
}
self.mqtt_client.publish(self.mqtt_publish_topic, json.dumps(obj), 1)
각 방별로 콘센트가 있으므로 기존 Room 클래스에 Outlet 리스트를 추가해주자
class Room:
name: str = 'Room'
# 각 방에는 조명 모듈 여러개와 난방 모듈 1개 존재
index: int = 0
lights: List[Light]
outlets: List[Outlet]
thermostat: Thermostat = None
def __init__(
self,
name: str = 'Room',
index: int = 0,
light_count: int = 0,
has_thermostat: bool = True,
outlet_count: int = 0,
**kwargs
):
self.name = name
self.index = index
self.lights = list()
self.outlets = list()
for i in range(light_count):
self.lights.append(Light(name=f'Light {i + 1}', index=i, room_index=self.index, mqtt_client=kwargs.get('mqtt_client')))
if has_thermostat:
self.thermostat = Thermostat(name='Thermostat', room_index=self.index, mqtt_client=kwargs.get('mqtt_client'))
for i in range(outlet_count):
self.outlets.append(Outlet(name=f'Outlet {i + 1}', index=i, room_index=self.index, mqtt_client=kwargs.get('mqtt_client')))
@property
def light_count(self):
return len(self.lights)
@property
def outlet_count(self):
return len(self.outlets)
Home 객체 초기화 시 outlet 객체들에 대한 정보도 다룰 수 있도록 하자
class Home:
rooms: List[Room]
# 중략
def __init__(self, room_info: List, name: str = 'Home'):
# 중략
self.rooms = list()
for i, info in enumerate(room_info):
name = info['name']
light_count = info['light_count']
has_thermostat = info['has_thermostat']
outlet_count = info['outlet_count']
self.rooms.append(Room(name=name, index=i, light_count=light_count, has_thermostat=has_thermostat, outlet_count=outlet_count, mqtt_client=self.mqtt_client))
# 중략
for room in self.rooms:
self.device_list.extend(room.lights)
if room.thermostat is not None:
self.device_list.append(room.thermostat)
self.device_list.extend(room.outlets)
self.device_list.append(self.gas_valve)
self.device_list.append(self.ventilator)
self.device_list.append(self.elevator)
# 중략
def load_config(self, filepath: str):
# 중략
node = root.find('rooms')
for i, room in enumerate(self.rooms):
room_node = node.find('room{}'.format(i))
if room_node is not None:
# 중략
for j in range(room.outlet_count):
outlet_node = room_node.find('outlet{}'.format(j))
if outlet_node is not None:
room.outlets[j].packet_set_state_on = outlet_node.find('on').text
room.outlets[j].packet_set_state_off = outlet_node.find('off').text
room.outlets[j].packet_get_state = outlet_node.find('get').text
mqtt_node = outlet_node.find('mqtt')
room.outlets[j].mqtt_publish_topic = mqtt_node.find('publish').text
room.outlets[j].mqtt_subscribe_topics.append(mqtt_node.find('subscribe').text)
# 중략
# 후략
그리고 Home 객체 초기화할 때 outlet 정보도 딕셔너리 안에 추가하도록 한다
(거실은 3개, 침실과 컴퓨터방은 각각 2개)
home = Home(room_info=[
{'name': 'Empty', 'light_count': 0, 'has_thermostat': False, 'outlet_count': 0},
{'name': 'Kitchen', 'light_count': 4, 'has_thermostat': True, 'outlet_count': 3},
{'name': 'Bedroom', 'light_count': 2, 'has_thermostat': True, 'outlet_count': 2},
{'name': 'Computer', 'light_count': 2, 'has_thermostat': True, 'outlet_count': 2}
], name='IPark-Gwanggyo')
home.initDevices()
마지막으로 Energy RS-485 패킷 파싱 구문에 Outlet 관련 코드를 추가해주자
def onParserEnergyResult(self, chunk: bytearray):
if len(chunk) < 8:
return
header = chunk[1] # [0x31, 0x41, 0x42, 0xD1]
command = chunk[3]
room_idx = 0
if header == 0x31:
if command in [0x81, 0x91]:
# 방 조명 패킷
room_idx = chunk[5] & 0x0F
room = self.rooms[room_idx]
for i in range(room.light_count):
dev = room.lights[i]
dev.state = (chunk[6] & (0x01 << i)) >> i
# notification
if dev.state != dev.state_prev or not dev.init:
dev.publish_mqtt()
dev.init = True
dev.state_prev = dev.state
# 콘센트 소비전력 패킷
for i in range(room.outlet_count):
dev = room.outlets[i]
dev.state = (chunk[7] & (0x01 << i)) >> i
if room_idx == 1 and i == 2:
dev.state = 1
if len(chunk) >= 14 + 2 * i + 2 + 1:
dev.measurement = int.from_bytes(chunk[14 + 2 * i: 14 + 2 * i + 2], byteorder='big') / 10.
else:
dev.measurement = 0
if int(dev.measurement) != int(dev.measurement_prev) or not dev.init:
dev.publish_mqtt()
dev.init = True
dev.measurement_prev = dev.measurement
elif command in [0x11]:
room_idx = chunk[5] & 0x0F
거실 (room index = 1)의 세 번째 소켓은 On/Off 제어가 불가능하므로 항상 state=1로 두었다
그리고 소비 전력이 바뀔 때마다 publish하도록 구현했는데, 소수점 단위로 바뀌면 너무 자주 notify해서 서버에 부하가 갈 것 같아 정수값이 바뀔 때마다 publish하도록 구현했다
2. Config XML 파일 수정
앞서 추출한 소켓 관련 On/Off 패킷들을 추가해주자
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<homenetworkserver>
<rooms>
<room1>
<outlet0>
<on>02 31 0D 01 FC 01 00 81 00 00 00 09 4F</on>
<off>02 31 0D 01 D8 01 00 01 00 00 00 00 EC</off>
<get>02 31 07 11 9B 01 C0</get>
<mqtt>
<publish>home/ipark/outlet/state/1/0</publish>
<subscribe>home/ipark/outlet/command/1/0</subscribe>
</mqtt>
</outlet0>
<!-- 후략 -->
3. Homebridge Config 파일 수정
Mqtthing 포맷에 맞춰 액세서리를 추가하면 된다
(거실/주방 첫번째 outlet 구현 예시)
{
"accessory": "mqttthing",
"type": "outlet",
"name": "Living room Outlet1 (MQTT)",
"url": "mqtt server url",
"username": "mqtt auth id",
"password": "mqtt auth password",
"topics": {
"getOn": {
"topic": "home/ipark/outlet/state/1/0",
"apply": "return JSON.parse(message).state;"
},
"setOn": {
"topic": "home/ipark/outlet/command/1/0",
"apply": "return JSON.stringify({state: message});"
},
"getWatts": {
"topic": "home/ipark/outlet/state/1/0",
"apply": "return JSON.parse(message).watts;"
}
},
"onValue": 1,
"offValue": 0,
"integerValue": false,
"history": true
}
getOn, setOn, getWatts 토픽만 제대로 적어주면 정상적으로 동작한다
소비전력은 floating 형태로 전송할 것이므로 'integetValue' 속성은 false로 하고, 소비전력 이력을 남기기 위해 'history' 속성을 true로 해준다
4. 액세서리 확인
홈브릿지를 재시작하고 액세서리가 제대로 추가되었는지 확인해보자
아이폰으로도 확인해보자
모두 켜져있는 것으로, 현재 상태가 제대로 표시되었다
이제 동작이 제대로 되는지 확인해보자!
[시리즈 링크]
광교아이파크::전원콘센트 Apple 홈킷 연동 (3) - Final
'홈네트워크(IoT) > 광교아이파크' 카테고리의 다른 글
Home Assistant - MQTT 액세서리 추가하기 (Bestin 홈네트워크 연동) (11) | 2022.02.18 |
---|---|
광교아이파크::전원콘센트 Apple 홈킷 연동 (3) - Final (4) | 2021.08.23 |
광교아이파크::전원콘센트 Apple 홈킷 연동 (1) (2) | 2021.08.23 |
광교아이파크::Bestin - Apple 홈킷 연동 소스코드 GitHub 업로드 (0) | 2021.07.25 |
광교아이파크::난방 온도값 파싱 오류 (0) | 2021.07.21 |