Notice
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 코스피
- 나스닥
- 미국주식
- MQTT
- 오블완
- Espressif
- 현대통신
- 퀄컴
- 홈네트워크
- 애플
- 엔비디아
- 해외주식
- esp32
- ConnectedHomeIP
- RS-485
- Home Assistant
- Apple
- homebridge
- 국내주식
- 배당
- 매터
- Bestin
- 티스토리챌린지
- 공모주
- 파이썬
- 힐스테이트 광교산
- 월패드
- matter
- Python
- raspberry pi
Archives
- Today
- Total
YOGYUI
광교아이파크::조명 Apple 홈킷 연동 (7) - Final 본문
반응형
[7] 최종 구현 결과
앞서 구현한 결과를 토대로 주방 조명 4개, 안방(침실) 조명 2개, 작은방(컴퓨터방) 조명 2개 모두 http-switch 액세서리로 등록한 후 display name을 적절하게 변경
Home 앱에서 스위치들을 각각 알맞는 방에 배정
스위치 모두 제대로 작동하고, Siri로 제어 가능한 것도 확인! (애플워치로도 시리 접근 가능하니 굉장히 편하다)
최소 비용으로 집안 조명을 모바일 기기랑 연동해서 제어할 수 있게 만들었다!!!
(분양받은 아파트로 이사가면 아예 모든 기기를 처음부터 Homekit이랑 연동할 수 있게 대공사할 계획)
지금까지 구현한 시스템의 schematic은 다음과 같다
구현 코드는 다음과 같다 (Git 업로드는 상황봐서 할 예정)
객체지향 코딩을 하긴 했는데 허접하긴 하다 (제대로 돌아가는거에 초점을 맞춰서...)
# common.py
from typing import List
class RoomInfo:
lights: List[bool]
lights_prev: List[bool]
lights_init: List[bool]
packet_light_on: List[str]
packet_light_off: List[str]
packet_status: List[str]
def __init__(self, light_count: int = 1):
self.lights = [False] * light_count
self.lights_prev = [False] * light_count
self.lights_init = [False] * light_count
self.packet_light_on = [''] * light_count
self.packet_light_off = [''] * light_count
self.packet_status = ''
@property
def light_count(self):
return len(self.lights)
# app.py
import os
import sys
import time
import requests
from typing import List
from flask import Flask, request, json, render_template
from common import RoomInfo
from SerialComm import SerialComm
from EnergyParser import EnergyParser
ser_energy = SerialComm()
PORT_485_ENERGY = '/dev/ttyUSB0'
# room 0: null, 1: bedroom, 2: pc room
rooms = [RoomInfo(), RoomInfo(4), RoomInfo(2), RoomInfo(2)]
rooms[1].packet_light_on[0] = '02 31 0D 01 D0 01 81 00 00 00 00 04 76'
rooms[1].packet_light_off[0] = '02 31 0D 01 D7 01 01 00 00 00 00 00 F5'
rooms[1].packet_light_on[1] = '02 31 0D 01 58 01 82 00 00 00 00 04 E9'
rooms[1].packet_light_off[1] = '02 31 0D 01 5F 01 02 00 00 00 00 00 6A'
rooms[1].packet_light_on[2] = '02 31 0D 01 5C 01 84 00 00 00 00 04 EF'
rooms[1].packet_light_off[2] = '02 31 0D 01 63 01 04 00 00 00 00 00 6C'
rooms[1].packet_light_on[3] = '02 31 0D 01 2B 01 88 00 00 00 00 04 94'
rooms[1].packet_light_off[3] = '02 31 0D 01 33 01 08 00 00 00 00 00 20'
rooms[1].packet_status = '02 31 07 11 9B 01 C0'
rooms[2].packet_light_on[0] = '02 31 0D 01 8C 02 81 00 00 00 00 04 3F'
rooms[2].packet_light_off[0] = '02 31 0D 01 93 02 01 00 00 00 00 00 B8'
rooms[2].packet_light_on[1] = '02 31 0D 01 7B 02 82 00 00 00 00 04 CB'
rooms[2].packet_light_off[1] = '02 31 0D 01 84 02 02 00 00 00 00 00 C4'
rooms[2].packet_status = '02 31 07 11 93 02 B5'
rooms[3].packet_light_on[0] = '02 31 0D 01 3B 03 81 00 00 00 00 04 97'
rooms[3].packet_light_off[0] = '02 31 0D 01 43 03 01 00 00 00 00 00 8B'
rooms[3].packet_light_on[1] = '02 31 0D 01 76 03 82 00 00 00 00 04 D5'
rooms[3].packet_light_off[1] = '02 31 0D 01 7E 03 02 00 00 00 00 00 49'
rooms[3].packet_status = '02 31 07 11 94 03 B1'
def parse_energy_result(chunk: bytearray):
try:
if len(chunk) < 7:
return
header = chunk[1]
command = chunk[3]
if header == 0x31 and command == 0x91:
room_idx = chunk[5] & 0x0F
room = rooms[room_idx]
for i in range(room.light_count):
room.lights[i] = bool(chunk[6] & (0x01 << i))
if room.lights[i] != room.lights_prev[i] or not room.lights_init[i]:
url = "http://0.0.0.0:-----/"
url += "switch_room{}_light{}?password=----".format(room_idx, i + 1)
json_obj = {"characteristic": "On", "value": room.lights[i]}
print('Notify: {}, {}'.format(url, json_obj))
requests.post(url, json=json_obj)
room.lights_init[i] = True
room.lights_prev[i] = room.lights[i]
except Exception as e:
pass
def checkSerialEnergyConn():
if not ser_energy.isConnected():
ser_energy.connect(PORT_485_ENERGY, 9600)
def sendSerialEnergyPacket(packet: str):
checkSerialEnergyConn()
ser_energy.sendData(bytearray([int(x, 16) for x in packet.split(' ')]))
class MyFlaskApp(Flask):
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
with self.app_context():
checkSerialEnergyConn()
super(MyFlaskApp, self).run(host=host, port=port, debug=debug, load_dotenv=load_dotenv, **options)
app = MyFlaskApp(__name__)
@app.route('/process', methods=['POST'])
def process():
response = ""
if not request.is_json:
req = request.get_data().decode(encoding='utf-8')
req = req.replace('\r', '')
req = req.replace('\n', '')
if 'json=' in req:
params = json.loads(req[5:])
else:
if req[0] == '{' and req[-1] == '}':
try:
params = json.loads(req)
except Exception:
params = {}
else:
params = {}
else:
params = json.loads(request.get_data(), encoding='utf-8')
print('POST json: {}'.format(params))
if 'room' in params.keys() and 'light' in params.keys():
room_idx = params['room']
light_idx = params['light']
if 'command' in params.keys():
if params['command'].lower() == 'on':
packet = rooms[room_idx].packet_light_on[light_idx]
target = True
else:
packet = rooms[room_idx].packet_light_off[light_idx]
target = False
while rooms[room_idx].lights[light_idx] != target:
sendSerialEnergyPacket(packet)
time.sleep(0.25)
sendSerialEnergyPacket(rooms[room_idx].packet_status)
time.sleep(0.25)
elif 'status' in params.keys():
response = '{}'.format(int(rooms[room_idx].lights[light_idx]))
return response
if __name__ == '__main__':
par_energy = EnergyParser(ser_energy)
par_energy.sig_parse.connect(parse_energy_result)
app.run(host='0.0.0.0', port=-----, debug=False)
ser_energy.release()
/* config.json */
{
"bridge": {
"name": "Homebridge 349E",
"username": "--:--:--:--:--:--",
"port": ----,
"pin": "XXX-XX-XXX"
},
"accessories": [
{
"accessory": "HTTP-SWITCH",
"name": "Kitchen Light1",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 0,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 0,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 0,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room1_light1",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "Kitchen Light2",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 1,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 1,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 1,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room1_light2",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "Kitchen Light3",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 2,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 2,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 2,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room1_light3",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "Kitchen Light4",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 3,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 3,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 1,
"light": 3,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room1_light4",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "Bedroom Light1",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 0,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 0,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 0,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room2_light1",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "Bedroom Light2",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 1,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 1,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 2,
"light": 1,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room2_light2",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "PC Room Light1",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 0,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 0,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 0,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room3_light1",
"notificationPassword": "----"
},
{
"accessory": "HTTP-SWITCH",
"name": "PC Room Light2",
"switchType": "stateful",
"onUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 1,
"command": "on"
},
"headers": {
"Accept": "application/json"
}
},
"offUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 1,
"command": "off"
},
"headers": {
"Accept": "application/json"
}
},
"statusUrl": {
"url": "http://localhost:----/process",
"method": "POST",
"body": {
"room": 3,
"light": 1,
"status": 0
},
"headers": {
"Accept": "application/json"
}
},
"notificationID": "switch_room3_light2",
"notificationPassword": "----"
},
],
"platforms": [
{
"name": "Config",
"port": ----,
"platform": "config"
}
]
}
TODO
-
거실 조명 제어
거실 조명들은 RS-485를 통해 제어되지 않는 것으로 보인다 (중앙 월패드에서만 제어 가능)
중앙 월패드 뜯어볼 예정 -
Control Device 제어
난방, 환기, 가스 제어 추가 (RS-485) -
기타
엘리베이터 호출, 도어락 열기 등 편의 기능 추가
[시리즈 링크]
반응형
'홈네트워크(IoT) > 광교아이파크' 카테고리의 다른 글
광교아이파크::난방 Apple 홈킷 연동 (2) (0) | 2021.01.02 |
---|---|
광교아이파크::난방 Apple 홈킷 연동 (1) (0) | 2021.01.02 |
광교아이파크::조명 Apple 홈킷 연동 (6) (0) | 2021.01.01 |
광교아이파크::조명 Apple 홈킷 연동 (5) (0) | 2021.01.01 |
광교아이파크::조명 Apple 홈킷 연동 (4) (0) | 2021.01.01 |