Дотянулись руки поэкспериментировать с умной лампочкой Yeelight от Xiaomi. Год пролежала без дела, и вот.
Краткая вводная: светодиодная лампочка вкручивается в патрон E27, подключается к домашней WiFi-сети и слушает команды со смартфона или устройства умного дома на включение и выключение, смену цвета и яркости.
Для Android существует пара официальных приложений: «Yeelight» и «MiHome». Оба приложения жутко китайские, требуют доступ к камере, микрофону, местоположению, контактам и ещё вороху других. Ставить такие анальныезонды на телефон, конечно, неохота.Есть и неофициальные приложения, но беглый поиск по Google Play ничего внятного не принёс. Решено: пишу свой вариант!
Ситуация осложняется тем обстоятельством, что из коробки лампочка заточена на работу с удалённым сервером. То есть буквально: вы стоите рядом с лампочкой и держите телефон в руке. Телефон отправляет команду на сервер в США или в Китай, и уже оттуда она возвращается обратно на лампочку. Перед тем, само собой, бережно сохранённая в ЦРУ или ГРУ НОАК. Как и пароль от вашей домашней закрытой WiFi-сети. И богу только известно, какую ещё информацию начнёт отсылать эта умная лампочка в час «Xэ».
Официальное приложение поставить-таки придётся ненадолго (и зарегистрировать Mi-аккаунт, и привязать к нему лампочку). Только оттуда можно активировать developer-режим, когда лампочка начинает слушать команды в локальной сети. Ни секунды лишней не стоит держать приложение установленным, а в роутерный брандмауэр хорошо бы добавить соответствующее правило на блокировку трафика по MAC-адресу лампочки. Я не параноик, но такой вот IoT с мутными забугорными серверами напрягает.
Ребята составили специальный документ для разработчиков, расписали там протоколы взаимодействия и примеры запросов.
Работа с лампочкой сводится, по сути, к двум приёмам.
Поиск¶
Опросить лампочки можно широковещательным запросом по UDP. Легко послать запрос утилиткой netcat (Линукс же):
cat request.txt | netcat -n -p 43210 -q1 -u -vv 239.255.255.250 1982
Содержимое request.txt
:
M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1982
MAN: "ssdp:discover"
ST: wifi_bulb
В ответ на указанный порт каждая лампочка пришлёт ответ: свои возможности и текущее состояние. Слушать тоже легко (надо запустить заранее, понятное дело):
netcat -l -p 43210 -u -vv
Кроме того, при подключении к WiFi-сети лампочка шлёт широковещательное оповещение о своём присутствии. Таким образом, можно не бомбить сеть опросами с телефона и экономить заряд аккумулятора.
Управление¶
У найденных лампочек нужно запомнить IP-адреса и порты, по которым позже слать управляющие команды, но уже по TCP. Снова в деле netcat:
echo -e '{"id": 1, "method": "toggle", "params": []}\r\n' | netcat -q1 -vv 192.168.1.146 55443
И вуаля! — ёлочка зажглась. 192.168.1.146
здесь — адрес лампочки у меня в сети. Команды составляются в формате JSON. Всё просто!
Ну, теперь дело за малым: развернуть новый проект в Android Studio, густо замешать Kotlin, RxJava2, Dagger2 и ButterKnife, и приложение готово.Я выложил исходники на GitHub, а в Google Play публиковать не стал, — приложение получилось довольно примитивное, умеет только включение-выключение лампочки и смену цвета. Развивать идею лениво, мне достаточно и этих возможностей.
Применение¶
Вручную управлять лампочкой, конечно, забавно, но концепция умного дома требует интеллектуальных автономных решений. Было придумано приспособить лампочку как световой будильник по утрам.
Концепция следующая: за полчаса до подъёма лампочка зажигается на минимальную яркость и цветовую температуру. Далее параметры нарастают экспоненциально, чтобы яркий свет не бил в глаза заранее, но постепенно наполнял комнату всё более холодным светом.
Поскольку управлять лампочкой можно по сети одной командой, а у меня круглосуточно включен роутер с OpenWRT на борту (считай, порезанный Линукс), я поставил задачи ему в crontab -e
. Сгенерировать список задач можно примерно таким скриптом на Python:
#!/usr/bin/env python
MINUTES = 30
MIN_BRIGHTNESS, MAX_BRIGHTNESS = 1, 100
MIN_TEMPERATURE, MAX_TEMPERATURE = 1700, 4000
print("30 6 * * 1-5 yeelight init 1")
for minute in range(0, MINUTES):
brightness = int((MAX_BRIGHTNESS - MIN_BRIGHTNESS) ** ((minute + 1) / float(MINUTES))+ MIN_BRIGHTNESS)
temperature = int((MAX_TEMPERATURE - MIN_TEMPERATURE) ** ((minute + 1) / float(MINUTES)) + MIN_TEMPERATURE)
print("%s 6 * * 1-5 yeelight temperature %s %s" % (30 + minute, brightness, temperature))
print("5 7 * * 1-5 yeelight temperature 100 3000")
Команда yeelight
здесь — самописный скрипт в /usr/bin/
:
#!/bin/sh
BULB="192.168.1.146 55443"
send_command() {
echo "Sending out $1"
if [ -f /bin/netcat ]; then
echo $1 | netcat -w1 -vv $BULB
else
echo -e $1 | nc $BULB
fi
}
if [ "$1" = "init" ]; then
send_command '{"id": 1, "method": "set_power", "params": ["on", "sudden", 0, '$2']}\r\n'
elif [ "$1" = "temperature" ]; then
send_command '{"id": 1, "method": "set_bright", "params": ['$2', "smooth", 3000]}\r\n'
send_command '{"id": 1, "method": "set_ct_abx", "params": ['$3', "smooth", 3000]}\r\n'
elif [ "$1" = "color" ]; then
send_command '{"id": 1, "method": "set_bright", "params": ['$2', "smooth", 3000]}\r\n'
send_command '{"id": 1, "method": "set_rgb", "params": ['$3', "smooth", 3000]}\r\n'
fi
В OpenWRT из коробки есть команда nc
— сильно облегчённый вариант netcat.
Лампочка как изделие выполнена весьма качественно, по крайней мере снаружи. На максимальной яркости светит примерно как светодиодная лампа 10Вт, ощутимо греется. Может быть, имеет смысл прикрепить к ней алюминиевый радиатор, если таковой окажется под рукой.
UPD: спустя почти год написал продолжение.