Интеграция пылесоса Roomba 980 в Умный дом Яндекса
Цель интеграции: через Алису запускать, останавливать и возвращать на базу пылесос iRobot Roomba 980.
Прямой интеграции Яндекса с iRobot нет. Есть интеграция Google с iRobot и отлично работает, но нет интеграции Яндекса с Google.
И не нашел ни одного брокера, который поддерживал бы и Яндек и iRobot - либо одно, либо другое.
Также с iRobot не заработал IFTTT,
интеграция с которым типа официально поддерживается.
Но на Roomb'е, как оказалось, есть MQTT сервер.
В конечном итоге сделал следующее
но до этого прошел несколько кругов всевозможных попыток сделать нормальную интеграцию.
Для того, чтобы узнать пароль на сервер MQTT:
- Сперва скачал и поставил Python на Windows
- Потом скачал Roomba980-Python
- С помощью getpassword.py узнал логин и пароль
Для проверки соединения скачал MQTT Explorer.
После нескольких неудачных попыток соединится с Roomba
погуглил и понял, что надо логин (blid) поставить также в MQTT Client ID.
Спасибо @mikee385
В качестве брокера MQTT попробовал, наверное самый популярный,
mosquitto.
Отдельно подписчик (mosquitto_sub) и издатель (mosquitto_pub) заработали, но запустить mosquitto в качестве брокера iRobot
никак не удалось. Писал что-то про неподдерживаемый протокол, хотя в подписке и издателе он прекрасно поддерживался.
Может это из-за того, что iRobot не распределяет сообщения по топикам, а гонит все в одну кучу. Может еще из-за чего нибудь.
Ни в форумах, ни в гугле не нашел решения и, как ни странно, даже попыток. Остановился на отдельной подписке и публикации.
В качестве web сервера установил lighttpd.
Поставил модули mod_auth, mod_authn_file и mod_cgi.
start.cgi
#!/bin/sh
mosquitto_pub -h roomba -p 8883 --insecure --cafile /root/roomba/roomba_ca.cer \
-i blid -u blid -P password \
-t cmd -m '{"command":"clean"}'
/bin/sh /root/roomba/status.sh
/bin/sh /root/roomba/status.cgi
|
dock.cgi
#!/bin/sh
/usr/bin/mosquitto_pub -h roomba -p 8883 --insecure --cafile /root/roomba/roomba_ca.cer \
-i blid -u blid -P password \
-t cmd -m '{"command":"dock"}'
/bin/sh /root/roomba/status.sh
/bin/sh /root/roomba/status.cgi
|
status.cgi
#!/bin/sh
status=`cat /root/roomba/status.txt`
if test "$status" == "charge" -o "$status" == "hmUsrDock"; then
echo '{"value":"0"}'
else
echo '{"value":"1"}'
fi
|
status.sh
mosquitto_sub \
-h roomba -p 8883 --insecure --cafile /root/roomba/roomba_ca.cer \
-i blid -u blid -P password \
-t roomba -C 15 |
grep cleanMissionStatus | tail -1 | sed s/'.*"phase":"\([^"]*\)*".*'/'\1'/ > /root/roomba/status.txt
|
Настроил Домовёнка Кузю.
Спасибо Алексею Старикову за проект.
На старт - clean.cgi, остановку - dock.cgi, проверка статуса - status.cgi.
Решение через mosquitto работало, но имело ряд существенных недостатков:
- Из-за того, что при каждой команде приходилось авторизовываться на roombe, вместе с проверкой статуса после выполнения команды,
продолжительность выполнения доходила до 10 сек.
- Не всегда команды доходили с первого, а иногда и со второго, раза
- Статус пылесоса отображал состояние после выполнения последней команды, а не реальное состояние.
Реальное состояние спрашивать было невозможно из-за той же большой задержки авторизации.
Решил попробовать dorita980,
на базе которого, между прочим, сделан и Roomba980-Python.
Спасибо @koalazak за проект и его поддержку.
- Установил Node.js на
OpenWrt. На этой системе у меня работает роутер в гостиной.
Для этого пришлось откатиться на версию 19.07.8, так как на 21.02 не оказалось пакета node.
- Установил также node-npm
- Установил через npm dorita980
- Там же естественно оказался getpassword.js, с помощью которого можно было узнать MQTT логин и пароль, но я его и так уже знал
Пришлось еще установить и инициализировать флешку и смонтировать ее в /opt,
так как все нужные пакеты во внутреннюю память не влезли, даже без node-npm.
В качестве web сервера использовал модуль Node.js http
roomba_basic.js
const http = require('http');
const port = порт_сервера;
const dorita980 = require('dorita980');
const roomba = new dorita980.Local('blid', 'password', 'roomba'); // robot name or IP address
roomba.on('connect', () => { console.log('server connected to roomba'); });
const server = http.createServer((req, res) => {
// проверка авторизации
if (req.headers.authorization == 'Basic авторизация_для_Кузи_в_Base64') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
// проверка статуса пылесоса
if (req.url == '/status') {
roomba.getRobotState().then((actualState) => {
var s = actualState['cleanMissionStatus']['phase'];
if (s == 'charge' || s == 'hmUsrDock') {
res.end('{"value":"0","phase":"'+s+'"}');
} else {
res.end('{"value":"1","phase":"'+s+'"}');
}
});
// команды пылесосу
} else if (req.url == '/clean') {
roomba.clean().then(res.end('{"value":"clean"}'));
} else if (req.url == '/stop') {
roomba.stop().then(res.end('{"value":"stop"}'));
} else if (req.url == '/dock') {
roomba.dock().then(res.end('{"value":"dock"}'));
} else res.end('{"value":"request is absent or unknown"}');
// запрос авторизации
} else {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="node.js"');
res.end('Unauthorized');
}});
// запуск http сервера
server.listen(port,() => {
console.log('server is running');
})
|
В Local Startup (/etc/rc.local) прописал /opt/usr/bin/node /opt/roomba_basic.js &.
И все заработало отлично. Выполнение команд и проверка реального статуса занимает 0.2-0.4 сек.!
|