반 년 전에 Orvibo S20이라는 스마트 소켓을 구입했다. 스마트 소켓은 스위치를 제어할 수 있도록 Wifi 모듈이 내장되어 있다. 스마트폰 앱을 사용해서 이 소켓의 전원을 올렸다 내렸다 할 수 있는데 집에 있는 거실 스탠드와 안방 스탠드에 연결해서 사용하고 있었다. Orvibo에서는 WiMo라는 앱을 제공하는데 앱이 상당히 부실하고 연결이 오락가락하는 문제점이 있었다. 또한 같은 wifi 네트워크 내에서만 동작하기 때문에 집을 나서면 불을 켜고 나왔는지 확인할 방법이 없었다. 이렇게 생각보다 활용도가 많이 떨어져서 제대로 쓰지 않고 방치하고 있었다.
어느 날 거실에서 방을 들어가는데 불을 끄고 스마트폰을 손전등처럼 쓰는 내 모습을 보고는 왜 사놓고 제대로 못쓰고 있나 생각이 확 들었다. 그래서 간단하게라도 만들어보기로 했다.
Orvibo에서는 딱히 개발을 위한 API를 공개하지 않았지만 S20 인터페이스를 리버싱해서 어떤 방식으로 제어할 수 있는지 분석한 사람들이 오픈소스 라이브러리로 내놓기 시작했다. 마침 집에서 방치하고 있던 라즈베리 파이가 있었고 최근 부지런히 사용하고 있는 텔레그램과 연동해서 아주 간단하게 외부에서 제어 가능한 환경을 만들 수 있었다.
사용한 라이브러리
텔레그램 봇을 만들 수 있는 파이썬 패키지는 엄청 많다. python-telegram-bot같은 멋진 라이브러리도 있지만 간단하게 데코레이터 문법으로 작성할 수 있는 pyTelegramBotAPI을 골랐다. 이 패키지는 간단한 기능만 제공하고 있어서 텔레그램의 고급 기능을 사용하려면 다른 패키지를 고르는 것이 낫다.
S20을 제어하는 패키지는 happyleavesaoc/python-orvibo를 사용했다. S20 자체에도 타이머나 추가적인 설정이 가능하지만 이 패키지는 그 기능을 지원하지 않는다. 그래도 자체 기능보다는 프로그램 쪽에서 제어하는게 편해서 제대로 켜고 끌 수만 있기만 하면 되서 이 패키지를 사용하게 되었다.
라우터 설정
라우터에서 설정할 부분은 크게 없었다. 텔레그램 봇은 네트워크 내에서 polling 해오는 방식이라서 별도로 포트를 포워딩해 외부 접근을 허용하거나 할 필요가 전혀 없었다. 대신 편의를 위해서 S20의 IP를 수동으로 할당해 내부에서 고정 IP로 접근할 수 있도록 변경했다.
텔레그램 봇 설정
봇은 예전에 만든 봇을 그대로 사용했다. 만드는 방법은 MS PowerShell에서 텔레그램 메시지 전송하기에서 확인할 수 있다.
코드 작성
멋지진 않지만 그래도 동작하는 코드를 아래처럼 작성했다.
from orvibo.s20 import S20
from telebot import Telebot
bot = TeleBot("<API KEY>")
living_room = ("Living", S20("192.168.3.1"))
bed_room = ("Bed", S20("192.168.3.2"))
def change_switch(s20, mode):
s20[1].on = mode
return status_message(s20)
def status_message(s20):
flag = "on" if s20[1].on else "off"
return "%s room light is %s" % (s20[0], flag)
@bot.message_handler(commands=["living_room_on"])
def living_room_on(message):
msg = change_switch(living_room, True)
bot.send_message(message.chat.id, msg)
@bot.message_handler(commands=["living_room_off"])
def living_room_off(message):
msg = change_switch(living_room, False)
bot.send_message(message.chat.id, msg)
@bot.message_handler(commands=["bed_room_on"])
def bed_room_on(message):
msg = change_switch(bed_room, True)
bot.send_message(message.chat.id, msg)
@bot.message_handler(commands=["bed_room_off"])
def bed_room_off(message):
msg = change_switch(bed_room, False)
bot.send_message(message.chat.id, msg)
@bot.message_handler(commands=["status"])
def all_statuses(message):
living_msg = status_message(living_room)
bed_msg = status_message(bed_room)
bot.send_message(message.chat.id, living_msg)
bot.send_message(message.chat.id, bed_msg)
if __name__ == '__main__':
bot.polling()
라즈베리파이 설정
봇에 문제가 생겨도 다시 실행되도록 Supervisor를 사용했다. pip을 사용해서 설치하려 했는데 python3 환경에선 사용하지 못한다고 나왔다. 대신 systemd를 추천 받아서 설치하려는데 라즈비안엔 없어서 고민했는데 apt-get에 Supervisor가 올라와 있어서 쉽게 해결할 수 있었다.
$ sudo apt-get install supervisor
supervisord.conf
를 간단하게 작성하고 실행했다. 전체 경로로 지정하고 싶지 않다면 PATH를 추가해도 된다.
[program:bot]
command=/home/pi/bot/.venv/bin/python /home/pi/bot/bot.py
$ supervisord -c /home/pi/bot/supervisord.conf
이제 봇에 말을 걸어보면 동작하는 것을 확인할 수 있다.
문제 해결
S20이 종종 연결이 끊어지는 문제가 있는데 S20으로부터 응답이 없으면 코드에 별도의 에러 핸들링이 없어서 봇이 재시작되었다. 이 경우에는 시간이 지나면 알아서 S20이 다시 붙거나 아니면 S20을 뽑았다 다시 끼워야 한다. 연결에 실패한 것이라도 확인할 수 있도록 코드를 조금 수정했다.
def wrong_message(s20):
return "%s room light is something wrong" % s20[0]
def change_switch(s20, mode):
try:
s20[1].on = mode
except:
return wrong_message(s20)
return status_message(s20)
def status_message(s20):
try:
flag = "on" if s20[1].on else "off"
except:
return wrong_message(s20)
return "%s room light is %s" % (s20[0], flag)
실제로 사용해보니 생각보다 조작을 많이 해야해서 타이머를 이용해 일종의 오토메이션 명령어를 추가했다.
import time
@bot.message_handler(commands=["go_to_bed"])
def go_to_bed(message):
bed_room_on(message)
bot.send_message(message.chat.id, "Living room light will turn off 15 secs after.")
time.sleep(15)
living_room_off(message)
얼마 전 iOS 10 업데이트를 하면서 Home 앱이 기본적으로 들어왔는데 애플의 HomeKit과 연동이 가능하면 사용할 수 있다고 한다. 이 프로토콜을 사용할 수 있도록 구현한 패키지가 nodejs에 존재하는데 HAP-NodeJS와 homebridge다. 이 도구와 연동하면 Siri를 이용해서 켜고 끌 수 있다는 장점이 있어서 여기서 작성한 코드를 조만간 연동 가능하게 만들어볼 생각이다.
각 장비에서 직접 API를 제공하거나 API를 사용해서 제어할 수 있는 방식에서 마이크로 서비스 아키텍처의 향기가 난다. 대부분의 홈 오토메이션 장비가 게이트웨이를 필요로 한다. 이 글에서는 라즈베리 파이를 썼고 애플의 HomeKit에서는 애플TV가 그런 역할을 한다. 마치 MSA의 API Gateway와 같은 방식으로 말이다. 앞으로 어떤 도구들이 재미있는 API를 들고 나올지 기대된다.