Yeelight

Дотянулись руки поэкспериментировать с умной лампочкой Yeelight от Xiaomi. Год пролежала без дела, и вот.figure

Краткая вводная: светодиодная лампочка вкручивается в патрон 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: спустя почти год написал продолжение.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поставьте галочки правильно (как бы защита от спама):

Я бот

Я не бот