[recovery mode] Управляем чайником SkyKettle из GNU/Linux
Вступительно
Довольно много даже и на Гиктаймс рекламируют технику компании Redmond которая поддерживает технологию R4S — Ready For Sky.
Все бы ничего, но техника эта управляется по Bluetooth со смартфона. И больше никак. Есть, говорят, вариант с каким-то шлюзом и управлением из облака…, но выставлять мой любимый чайник наружу — никакой радости.
Ситуация складывается странная. С одной стороны Ready For Sky состоит в консорциуме — allseenalliance, который, вроде бы, какой-то опенсорсный. С другой — ни фрагмента кода, ни строчки документации на протокол своего чайника я не видел. Я подозреваю что внутри что-то вроде чипа NORDIC SEMICONDUCTOR — и возможно надо читать доку на него.
Любой кто покупает технику с закрытыми протоколами совершает насилие над будущим.
Эта статья описывает первый шаг в управлении чайником в GNU/Linux — возможности включать и выключать его из консоли (с оговорками). Это важно потому, что если вам хочется чаю — вы не отрывая рук от клавиатуры ставите его кипятиться. Потом идете его пить. До этого — надо было искать смартфон и клацать в него пальцами — тут уж проще до чайника дойти. Еще один плюс — мультиплексирование доступа — пока можно держать только один смартфон подключенным к чайнику, а результаты консольного запроса можно транслировать в много мест.
Расширить этот подход до небольшого веб-приложения я думаю сможет каждый.
Как получить данные обмена
Начиная с андроида 4.4 кажется, в девелоперском меню появилась опция — Settings→Developer Options→Enable Bluetooth HCI snoop log (По умолчанию девелоперское меню скрыто). При включенной опции — всё общение телефона по Bluetooth логгируется в файл btsnoop_hci.log (имя файла в памяти телефона может отличаться в зависимости от настроек). Далее этот лог можно скопировать на PC и там проанализировать с помощью wireshark. Тот удобно и аккуратно показывает весь обмен, раскрывая и распаковывая пакеты.
Сценарий, таким образом, тривиален — работаем с чайником из штатного приложения, сохраняем лог, анализируем. Далее стоит задача — повторить обмен своими средствами, выдавая себя за телефон.Что представляет из себя протокол обмена
Я не большой спец по Bluetooth, но устройства Bluetooth Low Energy имеют несколько протоколов поверх собственно соединения — одним из них является протокол GATT/BT ATT — en.wikipedia.org/wiki/List_of_Bluetooth_protocols#Low_Energy_Attribute_Protocol_.28ATT.29, он в свою очередь надстроен поверх протокола L2CAP. Именно по BT ATT и общается чайник и смартфон.
Если просканировать и опросить чайник на предмет первичных сервисов, мы получим что-то вроде
gatttool -b E7:5A:53:79:82:A4 -t random --primary
- attr handle = 0×0001, end grp handle = 0×0007 uuid: 00001800–0000–1000–8000–00805f9b34fb Generic Access
- attr handle = 0×0008, end grp handle = 0×0008 uuid: 00001801–0000–1000–8000–00805f9b34fb Generic Attribute
- attr handle = 0×0009, end grp handle = 0xffff uuid: 6e400001-b5a3-f393-e0a9-e50e24dcca9e UART Service
Именно через последний сервис и будет вестись общение.
Теперь можно посмотреть все детальные возможности —
gatttool -b E7:5A:53:79:82:A4 -t random --characteristics
- handle = 0×0002, char properties = 0×0a, char value handle = 0×0003, uuid = 00002a00–0000–1000–8000–00805f9b34fb read write
- handle = 0×0004, char properties = 0×02, char value handle = 0×0005, uuid = 00002a01–0000–1000–8000–00805f9b34fb read
- handle = 0×0006, char properties = 0×02, char value handle = 0×0007, uuid = 00002a04–0000–1000–8000–00805f9b34fb read
- handle = 0×000a, char properties = 0×10, char value handle = 0×000b, uuid = 6e400003-b5a3-f393-e0a9-e50e24dcca9e notify read
- handle = 0×000d, char properties = 0×0c, char value handle = 0×000e, uuid = 6e400002-b5a3-f393-e0a9-e50e24dcca9e write
Меня несколько смущает отсутствие в перечислении handle 0×000c, который в общении используется. Ну да ладно.
Итак, как же идет общение:
— смартфон устанавливает соединение с чайником
— прочитывает, как и я только что, возможности чайника.
— потом инициирует общение, записывая по handle 0×000c (которого как будто при опросе и нет) значение 0×0100
— далее шлет чайнику команды по собственному протоколу (а может это протокол от NORDIC), и периодически опрашивает его состояние — все это поверх UART Service>ATT>BLE
Как этим управлять?
Вопрос естественным образом разделяется на аппаратную и программную составляющую. C первой все не сложно — я купил первый попавшийся донгл на ALI и он заработал — ru.aliexpress.com/item/Bluetooth-4–0-Dongles-Mini-USB-2–0–3–0-Bluetooth-Dongle-Adapters-Dual-Mode-adapter/32292553074.html
С программной частью гораздо сложнее. Собственно под GNU/Linux у нас нет выбора — BlueZ. И с этим есть проблемы.
Если я верно понимаю — типичный способ работы с BlueZ это обращение через DBUS. С/С++/Phyton биндинги, вроде и есть, но не для BT ATT протокола. Зато для него есть инструмент командной строки gatttool.
Казалось бы- это хорошо — инструмент опенсорсный, и можно посмотреть как он работает — code.metager.de/source/xref/linux/bluetooth/bluez/attrib. Но толку с исходников мало — он легко не отделяется от bluez и glib. Добивать и собрать его отдельно мне стало лень. А некие коррективы внести хотелось бы. Gatttool имеет инверсию абстракции — он прячет от пользователя установление соединения в «не интерактивном» режиме, и не поддается разумной автоматизации в «интерактивном». Более того с документацией в blueZ все очень плохо.
Чайник очень быстро разрывает соединение — в пределах секунды. А протокол требует сразу после переконнекта послать 0×0100 на хендл 0×000e — таким образом у нас остается два выбора —
1. Пытаться обернуть интерактивный режим gatttool и обращаться к нему — работающему в фоне через bash или python.
2. Посылать 0×0100 на хендл 0×000e всегда. В надежде что оно что-то там внутри включает, и это что-то не включится повторно лишний раз.
Первый подход я не осилил, хотя очень пытался, дело в том что gatttool использует для вывода не чистый stdout, а GNU Readline Library, и задача обертки по сложности сравнялась с задачей пересборки. Потому пошел вторым путем.
Итак возвращаясь к протоколу общения.
Настало время нырнуть в дебри.
Первым делом для управления чайника надо авторизоваться.
Смартфон для этого выбирает ключ и шлет последовательно посылки на хендл 0×000e и получает ответы с хендла 0×000b
SEND 55:00:ff: [8 байт ключа] :aa
RECV: 55:00:ff:00:aa
SEND 55:01:ff: [8 байт ключа] :aa
RECV: 55:01:ff:00:aa
SEND 55:02:ff: [8 байт ключа] :aa
RECV: 55:02:ff:00:aa
Из этого нехитрого обмена видно, что сообщения на хендл нумеруются и ответы приходят с тем же номером — для удобства идентификации. Каждый обмен начинается с 0×55 и кончается на 0xAA (полосатые числа). Обычно 3ий байт ответа повторяет номер команды.
Теперь надо дойти до чайника и нажать на нем кнопку »+» на 10 секунд. Так же как делали это со смартфоном. При этом вывод поменяется наSEND: 55:1e:ff: [8 байт ключа] :aa
RECV: 55:1e:ff:01:aa
Теперь так будет всегда при этом запросе. Ключ запомнен. Что за команды использует смартфон после авторизации?
Посылка сразу после включения — смысл не ясен до конца
SEND: 55: counter :01:aa
RECV: 55: counter :01:02:06:aa
Тут остается загадкой значение 4 и 5ого байтов.
Вскипятить и/или поддерживать
SEND: 55: counter :05:00:00:28:00:aa
SEND: 55: counter :05:00:00:00:00:aa - просто включить чайник.
SEND: 55: counter :05:01:00:5f:00:aa
RECV: 55: counter :05:01:aa // 01 status meens OK
Формат видимо такой: 55: counter :05:[надо ли поддерживать]:00: [температура поддержания] :00:aa
Не понятны мне пока 5, 7 байты запроса — ненулевыми я их не видел
Выключение
SEND: 55: counter :04:aa
RECV: 55: counter :04:01:aa // 01 status meens OK
Запросить состояние
SEND: 55: counter :06:aa
RECV: 55: counter :06:00:00:00:00:00:0c:00:00:00:00:51:00:00:00:00:00:aa - kettle is on in boiling
RECV: 55: counter :06:00:00:28:00:00:0c:00:01:02:00:33:00:00:00:00:00:aa - kettle is on
RECV: 55: counter :06:00:00:00:00:00:0c:00:00:00:00:3e:00:00:00:00:00:aa - kettle is off
RECV: 55: counter :06 00 00 28 00 00 0c 00 01 00 00 64 00 00 00 00 00 aa
RECV: 55: counter :06 01 00 28 00 00 0c 00 01 02 00 64 00 00 00 00 00 aa - kettle finished boiling
RECV: 55: counter :06 01 00 28 00 00 0c 00 01 02 00 63 00 00 00 00 00 aa - a while after finished boiling
общий вид:
55: counter :06: keep warm? :00: keepwarm temp? :00:00:0c:00:01: heater? :00: temp :00:00:00:00:00:aa
heater - нет полной уверенности, но состояние где-то в районе 10-12ого байтов.
- 0x00 - off
- 0x02 - on
temp
- градусы Цельсия текущей температуры (в чайнике)
keepwarm temp
- градусы Цельсия температуры, которую надо поддерживать
Большинство байт кроме температуры мне пока не понятно. Должен быть, наверное, датчик пустоты чайника.
Как этим пользоваться
— поставить себе на GNU/Linux bluez и проверить что у вас есть gatttool — он должен идти в комплекте.
— узнать MAC своего чайника вызвав
bt-device -l
— скачать скрипт из репозитория ниже
— выполнить одну из следующих команд (возможно вам надо иметь права писать в bluetooth)
(если еще не сделано — авторизовать приложение нажав »+» на чайнике.)
./connect.sh [KETTLE MAC] auth — просто ждет авторизации
./connect.sh [KETTLE MAC] query — запросить статус в цикле
./connect.sh [KETTLE MAC] queryone — запросить статус один раз
./connect.sh [KETTLE MAC] keeptemp — включить в режиме- вскипятить и поддерживать
./connect.sh [KETTLE MAC] on — включить
./connect.sh [KETTLE MAC] off — выключить
— помните что только одно устройство может контролировать чайник в каждый момент времени — если у вас запущено и активно приложение на смартфоне — ничего работать не будет.
Остаются проблемы
1. Не понятно когда происходит деавторизация. Очевидно не по реконнекту. Возможно по исчерпанию 255 запросов счетчика.
2. Код на bash завязан на задержи и возможны всякие гонки. Это ужасно. Надо все-таки выпотрошить gatttool и сделать аккуратную реализацию.
3. Возможно надо раскурочить чайник понять какого цвета у него потроха и что можно еще делать с ним по BT? может перешить и добавить туда что-то полезное?
4. Я вас всех авторизую жестко прописанным ключом… и потом смогу всеми вашими чайниками управлять, если вы мой сосед. Это все тоже надо поправить.
5. Расковырять все детали протокола. Нет ли уязвимостей? Где брать?
Ну и репозиторий сыренький — github.com/PimenovAlexander/r4s-bluetooth.
Там же и дампы которые можно посмотреть с помощью wireshark
Если у вас есть другие устройства R4S можно пробовать ломать и их. Если кто-то понимает в Bluetooth расскажите плиз откуда взялся этот 0×000с хендл.