일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 미국주식
- ConnectedHomeIP
- 배당
- Espressif
- 애플
- 매터
- Home Assistant
- MQTT
- matter
- 코스피
- 월패드
- RS-485
- 힐스테이트 광교산
- 국내주식
- 티스토리챌린지
- Apple
- 현대통신
- raspberry pi
- esp32
- 나스닥
- 오블완
- 해외주식
- Python
- 파이썬
- 퀄컴
- 홈네트워크
- Bestin
- homebridge
- 공모주
- 엔비디아
- Today
- Total
YOGYUI
광교아이파크::난방 Apple 홈킷 연동 (4) 본문
[4] Homebridge plug-in 설정
플러그인 키워드 thermostat으로 검색해보니 대다수 플러그인들은 Nest나 Honeywell같은 IoT 제품군들과 연계하여 사용해야 하는 것들이다
좀 더 찾아보니 @tommrodrigues가 업로드한 homebridge-web-thermostat가 HTTP기반 제어가 가능해 선택!
https://github.com/Tommrodrigues/homebridge-web-thermostat#readme
플러그인의 장점이자 단점이 액세서리별로 상태 변화에 대한 즉각적 업데이트가 가능한 listener 포트를 제공한다는 점
장점: notification-server 처럼 별도의 config 파일을 만들 필요가 없다
단점: 액세서리별로 단독 포트들이 오픈되니깐 부하가 걱정된다
또 다른 단점으로는 명령이나 상태 쿼리에 대해서 GET method 인자 방식만 매뉴얼에서 설명하고 있다는 점이다
액세서리별로 디바이스 온/오프 설정, 타겟 온도 설정, 상태값 쿼리 등에 대한 라우팅을 따로 둬야돼 코딩이 지저분해진다는 점인데...javascript 소스코드 훑어보고 정 안되면 직접 수정하는 걸로!
feasibility 확인할 겸 해서 플러그인 설치하고 매뉴얼대로 액세서리 추가
{
"accessory": "Thermostat",
"name": "Living room thermostat",
"apiroute": "http://localhost:9999/heat/room1",
"temperatureDisplayUnits": 0,
"currentRelativeHumidity": false,
"heatOnly": true,
"maxTemp": 40,
"minTemp": 5,
"minStep": 0.5,
"listener": true,
"port": 12345,
"manufacturer": "Bestin",
"serial": "",
"model": "Bestin"
},
방 1번 (거실/주방)에 대한 액세서리임을 명시, apiroute에 라우팅 부모경로를 지정해줘야 한다
cooling/heating 둘 다 지원하는지 여부, 온도 설정 최대값 및 최소값, 그리고 스텝값 등 필요한 속성들을 지정할 수 있다
액세서리에 명시한 대로 Flask 서버 코드도 수정
rooms[1].heat_listener_port = 12345
rooms[2].heat_listener_port = 12346
rooms[3].heat_listener_port = 12347
""" 중략 """
def parse_control_result(chunk: bytearray):
if len(chunk) < 10:
return
header = chunk[1]
command = chunk[3]
if header == 0x28 and command in [0x91, 0x92]:
room_idx = chunk[5] & 0x0F
room = rooms[room_idx]
if chunk[6] == 0x02:
room.heat = False
elif chunk[6] == 0x11:
room.heat = True
room.heat_temp_setting = (chunk[7] & 0x3F) + (chunk[7] & 0x40 > 0) * 0.5
room.heat_temp_current = chunk[9] / 10.0
# notification
if room.heat_prev != room.heat or room.heat_temp_setting != room.heat_temp_setting_prev or not room.heat_init:
if room_idx == 1:
url = "http://0.0.0.0:12345/"
url += "targetHeatingCoolingState?value={}".format(int(room.heat))
requests.get(url)
url = "http://0.0.0.0:12345/"
url += "targetTemperature?value={}".format(room.heat_temp_setting)
requests.get(url)
room.heat_init = True
room.heat_prev = room.heat
room.heat_temp_setting_prev = room.heat_temp_setting
""" 중략 """
@app.route('/heat/<room>/status', methods=['GET'])
def heat_room_get_status(room):
room_idx = int(room[-1])
heat = 1 if rooms[room_idx].heat else 0
obj = {
'targetHeatingCoolingState': heat,
'targetTemperature': rooms[room_idx].heat_temp_setting,
'currentHeatingCoolingState': heat,
'currentTemperature': rooms[room_idx].heat_temp_current
}
return jsonify(obj)
@app.route('/heat/<room>/targetHeatingCoolingState', methods=['GET'])
def heat_room_set_target_state(room):
# 변경된 상태 정보를 얻을 때까지 반복 전송
room_idx = int(room[-1])
value = request.args.get('value', default=1, type=int)
if value == 1:
while not rooms[room_idx].heat:
sendSerialControlPacket(rooms[room_idx].packet_heat_on)
time.sleep(0.25)
sendSerialControlPacket(rooms[room_idx].packet_heat_status)
time.sleep(0.25)
elif value == 0:
while rooms[room_idx].heat:
sendSerialControlPacket(rooms[room_idx].packet_heat_off)
time.sleep(0.25)
sendSerialControlPacket(rooms[room_idx].packet_heat_status)
time.sleep(0.25)
return ''
@app.route('/heat/<room>/targetTemperature', methods=['GET'])
def heat_room_set_target_temperature():
room_idx = int(room[-1])
value = request.args.get('value', default=20., type=float)
idx = max(0, min(70, int((value - 5.0) / 0.5)))
while rooms[room_idx].heat_temp_setting != value:
sendSerialControlPacket(rooms[room_idx].packet_heat_temp_set[idx])
time.sleep(0.25)
sendSerialControlPacket(rooms[room_idx].packet_heat_status)
time.sleep(0.25)
return ''
각 액세서리별로 /status, /targetHeatingCoolingState, /targetTemperature 세 종류의 라우팅을 뚫어줘야 제대로 동작한다( 동적 라우팅으로 공통화)
listener 포트에 메시지보낼때도 파라미터를 &로 묶어서 보내니 1번 파라미터만 응답한다
결국 On/Off 여부랑 현재 설정 온도 값에 대해서 두 번 나누어서 url 호출하도록 구현
소스코드 열어보니 substr(1)에 대해서만 핸들러 함수 태우게 구현해두었네? >> 나중에 필요하면 수정!
홈브릿지 재시작
홈앱에 thermostat 액세서리가 등장했다
(인증 동영상은 결국 패드들고 월패드 앞에서 찍음...)
켜기/끄기/타겟온도 설정값 변경 등 잘 동작함을 확인! (외부에서 상태 변경했을 경우 업데이트도 빠르게 잘 된다)
기능상 별다른 문제없이 잘 동작은 하는데, Status off일 경우 json으로 업데이트해준 현재 온도 (currentTemperature)를 상태값으로 표기해주는데 단위가 0.5℃ 단위로 반올림?되어서 나온다
(json 전송값이 22.3이나 22.2일때는 22.0으로 표시됨..)
Homekit 소스코드(github.com/homebridge/HAP-NodeJS/blob/186ce56fab55b5868142e27b59af6c5ab53038d6/src/lib/gen/HomeKit.ts#L811) current Temperature 객체 선언에 minStep은 분명히 0.1로 되어있는데... 플러그인 소스코드에서도 해당 속성 건드리는 부분을 찾을 순 없었다.
export class CurrentTemperature extends Characteristic {
static readonly UUID: string = '00000011-0000-1000-8000-0026BB765291';
constructor() {
super('Current Temperature', CurrentTemperature.UUID);
this.setProps({
format: Formats.FLOAT,
unit: Units.CELSIUS,
maxValue: 100,
minValue: 0,
minStep: 0.1,
perms: [Perms.READ, Perms.NOTIFY]
});
this.value = this.getDefaultValue();
}
}
홈브릿지 서버의 액세서리 탭에서는 정상적으로 0.1 단위로 표기되는거보니 애플 디바이스 홈 앱 자체의 특성같기도 하고...
[시리즈 링크]
'홈네트워크(IoT) > 광교아이파크' 카테고리의 다른 글
광교아이파크::환기(전열교환기) Apple 홈킷 연동 (1) (0) | 2021.01.04 |
---|---|
광교아이파크::난방 Apple 홈킷 연동 (5) - Final (0) | 2021.01.03 |
광교아이파크::난방 Apple 홈킷 연동 (3) (0) | 2021.01.02 |
광교아이파크::난방 Apple 홈킷 연동 (2) (0) | 2021.01.02 |
광교아이파크::난방 Apple 홈킷 연동 (1) (0) | 2021.01.02 |