главная страница
умный дом
  iRobot
  zigbee vs wi-fi
  лампа Сбер
  обогреватель
  tuya api

Интеграция пылесоса 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 сек.!