iRobot Roomba 980 integration

Moderator: leecollings

rmbrmb
Posts: 10
Joined: Monday 05 December 2016 9:05
Target OS: Raspberry Pi / ODroid
Domoticz version: Latest
Location: Netherlands
Contact:

iRobot Roomba 980 integration

Post by rmbrmb »

Hi,

I have an iRobot Roomba 980 robot vacuum cleaner, with wifi connection.
It is possible to control the robot via an app and I know how to control this robot via HTTP POST commands.
Can someone advise me how to accomplish this with Lua, or do I have to call Curl via the Lua script?
Also I want to query the status of the robot every 15 minutes, to check if the robot is back on it's base again.

If I use Curl to query the status, the command would look like (assume Roomba has IP 192.168.10.70);
curl -H "Content-Type: application/json" -H "Connection: close" -H "User-Agent: aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0" -H "Content-Encoding: identity" -H "Accept: */*" -H "Accept-Language: en-us" -H "Host: 192.168.10.70" -H "Authorization: Basic [base64-password]" -X POST -d '{"do":"get","args":["mssn"],"id":1}' -k https://192.168.10.70/umi

Then I get (for example) the following response;
{"ok":{"flags":0,"cycle":"none","phase":"charge","pos":{"theta":179,"point":{"x":221,"y":-12}},"batPct":97,"expireM":0,"rechrgM":0,"error":0,"notReady":0,"mssnM":0,"sqft":0},"id":1}

"cycle":"none" tells me the robot is not driving around.
"phase":"charge" tells me the robot is on it's base station, charging.
Also the "error" and "notReady" fields are possibly interesting to monitor.

I would like to query these fields with a Lua script and put them individually in Domoticz variables for further processing.

There is a lot more possible with HTTP POST commands, so if someone is able to write a plugin for full integration of the Roomba 980 in Domoticz that would be even better!

Start a cleaning-job:

POST https://192.168.10.70/umi
Content-Type: application/json
Connection: close
User-Agent: aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0
Content-Encoding: identity
Accept: */*
Accept-Language: en-us
Host: 192.168.10.70
Authorization: Basic [base64-password]

{"do":"set","args":["cmd" {"op":"start"}],"id":1}

Pause the cleaning-job (replace the commands above with):
{"do":"set","args":["cmd" {"op":"pause"}],"id":1}

Resume the cleaning-job (replace the commands above with):
{"do":"set","args":["cmd" {"op":"resume"}],"id":1}

Stop the cleaning-job (replace the commands above with):
{"do":"set","args":["cmd" {"op":"stop"}],"id":1}

Return to base (replace the commands above with):
{"do":"set","args":["cmd" {"op":"dock"}],"id":1}

You will need to get the uniq device password (replace field [base64-password] above, without brackets) and that can be a bit difficult.
Please check the article on the following page to accomplish that;
https://community.smartthings.com/t/roo ... g/44860/93

For more possible commands, please check the source code of the following project;
https://github.com/koalazak/dorita980/

Kind regards,
Rene.
rmbrmb
Posts: 10
Joined: Monday 05 December 2016 9:05
Target OS: Raspberry Pi / ODroid
Domoticz version: Latest
Location: Netherlands
Contact:

Re: iRobot Roomba 980 integration

Post by rmbrmb »

So far I have accomplished this with a bash script, but I can imagine this can be done prettier;

Code: Select all

#!/bin/bash

function jsonValue()
{
key=$1
num=$2
awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'$key'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p
}

output=`curl -s -H "Content-Type: application/json" -H "Connection: close" -H "User-Agent: aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0" -H "Content-Encoding: identity" -H "Accept-Language: en-us" -H "Host: 192.168.10.70" -H "Authorization: Basic [base64-password]" -X POST -d '{"do":"get","args":["mssn"],"id":1}' -k https://192.168.10.70/umi`

RoombaActivity=`echo $output | jsonValue cycle 1`
RoombaStatus=`echo $output | jsonValue phase 1`

if [ "$RoombaActivity" == "none" ]; then
   RoombaActivity='Geen'
else
   RoombaActivity='Bezig'
fi

if [ "$RoombaStatus" == "charge" ]; then
   RoombaStatus='Opladen'
elif [ "$RoombaStatus" == "stop" ]; then
   RoombaStatus='Gestopt'
else
   RoombaStatus='Bezig'
fi

echo "Roomba Activiteit   :" $RoombaActivity
echo -n "Bijgewerkt Domoticz :" && curl -s "http://127.0.0.1:8080/json.htm?type=command&param=updateuservariable&vname=RoombaActivity&vtype=2&vvalue=$RoombaActivity" | jsonValue status 1
echo "Roomba Status       :" $RoombaStatus
echo -n "Bijgewerkt Domoticz :" && curl -s "http://127.0.0.1:8080/json.htm?type=command&param=updateuservariable&vname=RoombaStatus&vtype=2&vvalue=$RoombaStatus" | jsonValue status 1
BakSeeDaa
Posts: 485
Joined: Thursday 17 September 2015 10:13
Target OS: Raspberry Pi / ODroid
Domoticz version:

Re: iRobot Roomba 980 integration

Post by BakSeeDaa »

rmbrmb wrote:Hi,

I have an iRobot Roomba 980 robot vacuum cleaner, with wifi connection.
...
Thanks for sharing this @Rene

I have one that will be delivered to me tomorrow and of course it needs to be automated. Awesome!

Cheers
rmbrmb
Posts: 10
Joined: Monday 05 December 2016 9:05
Target OS: Raspberry Pi / ODroid
Domoticz version: Latest
Location: Netherlands
Contact:

Re: iRobot Roomba 980 integration

Post by rmbrmb »

You are welcome.

The only problem at this moment is that iRobot has pushed firmware 2.x, and since this release they have changed the communication protocol completely. The iRobot is now using MQTT in stead of HTTP with firmware 1.x.

There are more people looking for a solution... See also:
https://community.smartthings.com/t/roo ... /44860/144
BakSeeDaa
Posts: 485
Joined: Thursday 17 September 2015 10:13
Target OS: Raspberry Pi / ODroid
Domoticz version:

Re: iRobot Roomba 980 integration

Post by BakSeeDaa »

rmbrmb wrote:You are welcome.

The only problem at this moment is that iRobot has pushed firmware 2.x, and since this release they have changed the communication protocol completely. The iRobot is now using MQTT in stead of HTTP with firmware 1.x.

There are more people looking for a solution... See also:
https://community.smartthings.com/t/roo ... /44860/144
I've installed the dorita980: unofficial iRobot Roomba 980 NodeJS library / SDK and it's firmware 2.0.0 compatible. I installed it on a different Raspberry Pi, not the one that I use for Domoticz. The HTTP REST API interface is not firmware 2.0.0 compatible yet.

Cheers!
Last edited by BakSeeDaa on Friday 24 February 2017 9:46, edited 2 times in total.
BakSeeDaa
Posts: 485
Joined: Thursday 17 September 2015 10:13
Target OS: Raspberry Pi / ODroid
Domoticz version:

Re: iRobot Roomba 980 integration

Post by BakSeeDaa »

Below is my current working solution for a RaspBerry Pi. It might be improved in many ways but I will await further development of the dorita980: unofficial iRobot Roomba 980 NodeJS library / SDK and the HTTP REST API interface before doing more.

First install the dorita980: unofficial iRobot Roomba 980 NodeJS library / SDK

Set up a Node.js-environment on your Raspberry Pi if You haven't done that already.

Create a dummy Text Device in Domoticz and name it "Adolf Status". (Yes, my iRobot Roomba 980 is named Adolf. Your iRobot Roomba 980 may be named differently.)

Optionally create a dummy Switch Device in Domoticz and name it "schAdolf". Assign a schedule to it and enter when the iRobot Roomba 980 is allowed to autostart)

Created a couple of dummy switches (Adolf Start, Adolf Stop, Adolf Pause, Adolf Resume), entered a 2 second delay off on each switch and put some LUA code into work. Then I made the following LUA script:

script_device_adolf.lua (Your tmp directory might be /tmp instead of /var/tmp Change if necessary!)

Code: Select all

commandArray = {}

if (devicechanged['Adolf Start'] == 'On') then -- Virtual switch to set on/off state
	os.execute('echo -ne start > /var/tmp/adolf-cmd > /dev/null 2>&1 &')
end

if (devicechanged['Adolf Stop'] == 'On') then -- Virtual switch to set on/off state
	os.execute('echo -ne stop > /var/tmp/adolf-cmd > /dev/null 2>&1 &')
end

if (devicechanged['Adolf Pause'] == 'On') then -- Virtual switch to set on/off state
	os.execute('echo -ne pause > /var/tmp/adolf-cmd > /dev/null 2>&1 &')
end

if (devicechanged['Adolf Resume'] == 'On') then -- Virtual switch to set on/off state
	os.execute('echo -ne resume > /var/tmp/adolf-cmd > /dev/null 2>&1 &')
end

if (devicechanged['Adolf Dock'] == 'On') then -- Virtual switch to set on/off state
	os.execute('echo -ne dock > /var/tmp/adolf-cmd > /dev/null 2>&1 &')
end

return commandArray
Create a user variable in Domoticz named roomba980LastRun

Create the following shell script:

/home/pi/domoticz/scripts/sh/adolfGetMission.sh

Code: Select all

#!/bin/bash

if pidof -x "adolf-getMission.js" >/dev/null; then
	echo "Adolf getMission process already running"
	exit 0
fi

node /home/pi/domoticz/scripts/js/adolf-getMission.js &
Create the following script:

home/pi/domoticz/scripts/js/adolf-getMission.js (Your tmp directory might be /tmp instead of /var/tmp Change if necessary!)

Code: Select all

#!/usr/bin/nodejs
var dorita980 = require('dorita980');
var request = require("request");
var fs = require('fs');
var domoHostAndPort = 'http://DOMOUSERNAME:DOMOPASSW@DOMOHOSTNAME:8080';
var domoTextDeviceIdx = 503; // Change this
var cleanMissionStatus = 5000; // default is 800ms
var myRobotViaLocal = new dorita980.Local('MyUsernameBlid', 'MyPassword', '192.168.4.70', 2, cleanMissionStatus);
var lastPhase = '';

myRobotViaLocal.on('mission', function (data) {
	var phase = data.cleanMissionStatus.phase;
	//console.log(phase);
	if (phase != lastPhase) {
		lastPhase = phase;
		tellDomoticz('Adolf', phase, domoTextDeviceIdx);
	}
	var cmdfile = '/var/tmp/adolf-cmd';
	fs.exists(cmdfile, function(exists) {
		if(exists) {
			var contents = fs.readFileSync(cmdfile, 'utf8');
			console.log('Received command: ' + contents);
			if (contents == 'start') myRobotViaLocal.start();
			if (contents == 'stop') myRobotViaLocal.stop();
			if (contents == 'dock') myRobotViaLocal.dock();
			if (contents == 'pause') myRobotViaLocal.pause();
			if (contents == 'resume') myRobotViaLocal.resume();
			fs.unlinkSync(cmdfile);
		}
	});
});

function getDateTime() {
	var date = new Date();
	var hour = date.getHours();
	hour = (hour < 10 ? "0" : "") + hour;
	var min = date.getMinutes();
	min = (min < 10 ? "0" : "") + min;
	var sec = date.getSeconds();
	sec = (sec < 10 ? "0" : "") + sec;
	var year = date.getFullYear();
	var month = date.getMonth() + 1;
	month = (month < 10 ? "0" : "") + month;
	var day = date.getDate();
	day = (day < 10 ? "0" : "") + day;
	return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}

function tellDomoticz(robotName, newText, idx) {
	if (newText == 'run') {
		request(domoHostAndPort + "/json.htm?type=command&param=saveuservariable&vname=roomba980LastRun&vtype=2&vvalue=" + newText, function(error, response, body) {
		console.log(getDateTime() + ' Storing last run info for ' + robotName + ' : ' + response.statusCode);
	})
	}
	request(domoHostAndPort + "/json.htm?type=command&param=udevice&idx=" + idx + "&nvalue=0&svalue=" + newText, function(error, response, body) {
	console.log(getDateTime() + ' ' + robotName + ' ' + newText + ': ' + response.statusCode);
	})
}
Assign proper permissions

Code: Select all

chmod 755 /home/pi/domoticz/scripts/js/adolf-getMission.js
chmod 755 /home/pi/domoticz/scripts/sh/adolfGetMission.sh
Try if it's working:

Code: Select all

/usr/bin/nodejs /home/pi/domoticz/scripts/js/adolf-getMission.js
When it works, make a crontab entry:

Code: Select all

*/10 * * * * /home/pi/domoticz/scripts/sh/adolfGetMission.sh
Wait 10 minutes and check if the process is up and running

Code: Select all

ps -ef|grep dolf
Below some LUA code that I use in my system to autostart the iRobot Roomba 980 when I leave the premises. It's here just to give You some ideas. You will not be able to use exactly the same LUA script in your system.

Code: Select all

commandArray = {}
-- Start Adolf automatically ?
if ((devicechanged['Z1 Alarm'])
or (devicechanged['schAdolf'] == 'On')) then
	if ((otherdevices_svalues['Z1 Alarm'] == 'Arm Away')
	and (otherdevices['schAdolf'] == 'On')) then
		getMyDomo()
		-- Has it been more than 36 hours since last time Adolf went out?
		if (mydomo.utils.timedifference(uservariables_lastupdate['roomba980LastRun']) > (3600*36) ) then
			if (otherdevices['Adolf Status'] ~= 'run') then
				commandArray['Adolf Start'] = 'On'
			end
		end
	end
end

-- Pause Adolf if necessary when I'm engaged in a phone conversation
if (devicechanged['PhoneConversation'] or devicechanged['A580 IP']) then
	local switchCmd = 'Off'
	if (((otherdevices['PhoneConversation'] == 'On') or (otherdevices['A580 IP'] == 'On'))
	and (otherdevices_svalues['Adolf Status'] == 'run')) then
		commandArray['Adolf Pause'] = 'On'
	elseif (((otherdevices['PhoneConversation'] == 'Off') and (otherdevices['A580 IP'] == 'Off'))
	and (otherdevices_svalues['Adolf Status'] == 'stop')) then
		commandArray['Adolf Resume'] = 'On'
	end
end

return commandArray
Last edited by BakSeeDaa on Friday 24 February 2017 10:30, edited 5 times in total.
rmbrmb
Posts: 10
Joined: Monday 05 December 2016 9:05
Target OS: Raspberry Pi / ODroid
Domoticz version: Latest
Location: Netherlands
Contact:

Re: iRobot Roomba 980 integration

Post by rmbrmb »

Thanks for sharing!
BakSeeDaa
Posts: 485
Joined: Thursday 17 September 2015 10:13
Target OS: Raspberry Pi / ODroid
Domoticz version:

Re: iRobot Roomba 980 integration

Post by BakSeeDaa »

rmbrmb wrote:Thanks for sharing!
The code that I shared above has been updated. :D
Drago85
Posts: 1
Joined: Tuesday 05 December 2017 10:33
Target OS: Linux
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by Drago85 »

Hello all,

I recently bougth an iRobot Roomba (896) and was looking at this post to include the Roomba to my Domoticz environment.
My Roomba was delivered with a newer firmware than dorita980 supports (in my case 3.2.7) and therefor the software/code needed some changes.

I wanted to share my code for people with the same "problem".

We need to change the local.js file from dorita980 because the variable "pose" does not exist in the 896 3.2.7 software.
Changes to "dorita980/lib/v2/local.js":

Code: Select all

'use strict';

const mqtt = require('mqtt');

var dorita980 = function localV2 (user, password, host, emitIntervalTime) {
  if (!user) throw new Error('robotID is required.');
  if (!password) throw new Error('password is required.');
  if (!host) throw new Error('host is required.');

  emitIntervalTime = emitIntervalTime || 800;
  var robotState = {};
  var missionInterval;

  const url = 'tls://' + host;

  var options = {
    port: 8883,
    clientId: user,
    rejectUnauthorized: false,
    protocolId: 'MQTT',
    protocolVersion: 4,
    clean: false,
    username: user,
    password: password
  };

  const client = mqtt.connect(url, options);

  client.on('error', function (e) {
    throw e;
  });

  client.on('connect', function () {
    missionInterval = setInterval(() => {
      if (robotState.cleanMissionStatus) { //) { && robotState.pose) {
        client.emit('mission', filterProps(['cleanMissionStatus', 'bin'])); //'pose', 'bin']));
      }
    }, emitIntervalTime);
  });

  client.on('close', function () {
    clearInterval(missionInterval);
  });

  client.on('packetreceive', function (packet) {
    if (packet.payload) {
      try {
        const msg = JSON.parse(packet.payload.toString());
        robotState = Object.assign(robotState, msg.state.reported);
        client.emit('update', msg);
        client.emit('state', robotState);
      } catch (e) {}
    }
  });

  function _apiCall (topic, command) {
    return new Promise((resolve, reject) => {
      let cmd = {command: command, time: Date.now() / 1000 | 0, initiator: 'localApp'};
      if (topic === 'delta') {
        cmd = {'state': command};
      }
      client.publish(topic, JSON.stringify(cmd), function (e) {
        if (e) return reject(e);
        resolve({ok: null}); // for retro compatibility
      });
    });
  }

function hasAllProps (obj, properties) {
    for (var p in properties) {
      if (!obj.hasOwnProperty(properties[p])) {
        return false;
      }
    }
    return true;
  }

  function filterProps (properties) {
    let ret = {};
    if (properties.length === 1) return robotState[properties[0]];
    for (var p in properties) {
      ret[properties[p]] = robotState[properties[p]];
    }
    return ret;
  }

  function waitPreferences (decode, waitFor, returnOnlyThat) {
    waitFor = (typeof waitFor === 'string') ? [waitFor] : waitFor;

    return new Promise((resolve) => {
      var checkInterval = setInterval(() => {
        if (hasAllProps(robotState, waitFor)) {
          clearInterval(checkInterval);
          resolve(returnOnlyThat ? filterProps(waitFor) : robotState);
        }
      }, 100);
    });
  }

  return Object.assign(client, {
    getTime: () => waitPreferences(false, ['utctime'], true),
    getBbrun: () => waitPreferences(false, ['bbrun'], true),
    getLangs: () => waitPreferences(false, ['langs'], true),
    getSys: () => waitPreferences(false, ['bbrstinfo', 'cap', 'sku', 'batteryType', 'soundVer', 'uiSwVer', 'navSwVer', 'wifiSwVer', 'mobilityVer', 'bootloaderVer', 'umiVer', 'softwareVer', 'audio', 'bin'], true),
    getWirelessLastStatus: () => waitPreferences(false, ['wifistat', 'wlcfg'], true),
    getWeek: () => waitPreferences(false, ['cleanSchedule'], true),
    getPreferences: (decode) => waitPreferences(decode, ['cleanMissionStatus', 'cleanSchedule', 'name', 'vacHigh', 'pose', 'signal'], false),
    getRobotState: (fields) => waitPreferences(false, fields, false),
    getMission: (decode) => waitPreferences(decode, ['cleanMissionStatus', 'pose', 'bin', 'batPct'], true),
    getWirelessConfig: () => waitPreferences(false, ['wlcfg', 'netinfo'], true),
    getWirelessStatus: () => waitPreferences(false, ['wifistat', 'netinfo'], true),
    getCloudConfig: () => waitPreferences(false, ['cloudEnv'], true),
    getSKU: () => waitPreferences(false, ['sku'], true),
    start: () => _apiCall('cmd', 'start'),
    pause: () => _apiCall('cmd', 'pause'),
    stop: () => _apiCall('cmd', 'stop'),
    resume: () => _apiCall('cmd', 'resume'),
    dock: () => _apiCall('cmd', 'dock'),
    setWeek: (args) => _apiCall('delta', {cleanSchedule: args}),
    setPreferences: (args) => _apiCall('delta', args),
    setCarpetBoostAuto: () => _apiCall('delta', {'carpetBoost': true, 'vacHigh': false}),
    setCarpetBoostPerformance: () => _apiCall('delta', {'carpetBoost': false, 'vacHigh': true}),
    setCarpetBoostEco: () => _apiCall('delta', {'carpetBoost': false, 'vacHigh': false}),
    setEdgeCleanOn: () => _apiCall('delta', {'openOnly': false}),
    setEdgeCleanOff: () => _apiCall('delta', {'openOnly': true}),
    setCleaningPassesAuto: () => _apiCall('delta', {'noAutoPasses': false, twoPass: false}),
    setCleaningPassesOne: () => _apiCall('delta', {'noAutoPasses': true, twoPass: false}),
    setCleaningPassesTwo: () => _apiCall('delta', {'noAutoPasses': true, twoPass: true}),
    setAlwaysFinishOn: () => _apiCall('delta', {'binPause': false}),
    setAlwaysFinishOff: () => _apiCall('delta', {'binPause': true})
  });
};

module.exports = dorita980;
We also need to change the "-getMission.js", besides the "pose" we need to change seeing the variable does not exist we also need to change the code so we don't leave any open streams to our Roomba.
As a bonus we will create a file that stores the last phase so we won't keep updating it unnecessarily.
FYI my Roomba is called Dusty so change the settings to your liking:

Code: Select all

#!/usr/bin/nodejs
var dorita980 = require('/usr/lib/node_modules/dorita980');
var request = require('/usr/lib/node_modules/dorita980/node_modules/request');
var fs = require('fs');
var domoHostAndPort = 'http://admin:DomoPassword@IP-Address:Port';
var domoTextDeviceIdx = 16; //Device idx of the Roomba state device
var cleanMissionStatus = 5000;
var myRobotViaLocal = new dorita980.Local('MyUsernameBlid', 'MyPassword', 'IP-Address', 2, cleanMissionStatus);
var lastPhaseFile = '/usr/local/domoticz/stateFiles/dustyLastPhase';
var lastPhase = fs.readFileSync(lastPhaseFile, 'utf8');
var cmdfile = '/var/tmp/dusty-cmd';

myRobotViaLocal.on('mission', function (data) {
        var phase = data.cleanMissionStatus.phase;
        if (phase != lastPhase) {
                lastPhase = phase;
		fs.writeFile(lastPhaseFile, lastPhase, function(err) {
    			if(err) {
			        return console.log(err);
			}

		    	console.log("Dusty current state saved in state file!");
		}); 
                tellDomoticz('Dusty', phase, domoTextDeviceIdx);
        }
	else {
		console.log('Dusty Phase same as before = ' + phase);
	}
        fs.exists(cmdfile, function(exists) {
                if(exists) {
                        var contents = fs.readFileSync(cmdfile, 'utf8');
                        console.log('Received command: ' + contents);
                        if (contents == 'start'){
                                myRobotViaLocal.start()
                                .then(() => myRobotViaLocal.end()) // disconnect to leave the channel open for the mobile app.
                                .catch(console.log);
                        }
                        if (contents == 'stop'){
                                myRobotViaLocal.stop()
                                .then(() => myRobotViaLocal.end()) // disconnect to leave the channel open for the mobile app.
                                .catch(console.log);
                        }
                        if (contents == 'dock'){
                                myRobotViaLocal.dock()
                                .then(() => myRobotViaLocal.end()) // disconnect to leave the channel open for the mobile app.
                                .catch(console.log);
                        }
                        if (contents == 'pause'){
                                myRobotViaLocal.pause()
                                .then(() => myRobotViaLocal.end()) // disconnect to leave the channel open for the mobile app.
                                .catch(console.log);
                        }
                        if (contents == 'resume'){
                                myRobotViaLocal.resume()
                                .then(() => myRobotViaLocal.end()) // disconnect to leave the channel open for the mobile app.
                                .catch(console.log);
                        }
                        fs.unlinkSync(cmdfile);
                }
                myRobotViaLocal.end();
        });
 });


function getDateTime() {
        var date = new Date();
        var hour = date.getHours();
        hour = (hour < 10 ? "0" : "") + hour;
        var min = date.getMinutes();
        min = (min < 10 ? "0" : "") + min;
        var sec = date.getSeconds();
        sec = (sec < 10 ? "0" : "") + sec;
        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        month = (month < 10 ? "0" : "") + month;
        var day = date.getDate();
        day = (day < 10 ? "0" : "") + day;
        return year + "-" + month + "-" + day + " " + hour + ":" + min + ":" + sec;
}

function tellDomoticz(robotName, newText, idx) {
        if (newText == 'run') {
                request(domoHostAndPort + "/json.htm?type=command&param=updateuservariable&vname=dustyLastRun&vtype=2&vvalue=" + newText, function(error, response, body) {
                console.log(getDateTime() + ' Storing last run info for ' + robotName + ' : ' + response.statusCode);
        })
        }
        request(domoHostAndPort + "/json.htm?type=command&param=udevice&idx=" + idx + "&nvalue=0&svalue=" + newText, function(error, response, body) {
        console.log(getDateTime() + ' ' + robotName + ' ' + newText + ': ' + response.statusCode);
        })
}
I changed the crontab as well seeing I don't want to check the status at night because the Roomba will light up and we have pets:

Code: Select all

*/10 06-19 * * * /usr/local/domoticz/scripts/sh/dustyGetMission.sh
Hope this helps,

Drago
echeberri77
Posts: 39
Joined: Friday 09 March 2018 18:52
Target OS: Linux
Domoticz version: 3.9203
Location: Italy
Contact:

Re: iRobot Roomba 980 integration

Post by echeberri77 »

Hi BakSeeDaa, I'd like to post some questions about your configuration if it is possible. Thanks!
BakSeeDaa wrote: Thursday 23 February 2017 11:28 Create a dummy Text Device in Domoticz and name it "Adolf Status". (Yes, my iRobot Roomba 980 is named Adolf. Your iRobot Roomba 980 may be named differently.)
My roomba980 is named Vicky. I managed to create Vicky Status under Configuration -> Device. I created it as: Dummy (Does nothing, use for virtual switches only).
BakSeeDaa wrote: Thursday 23 February 2017 11:28 Created a couple of dummy switches (Adolf Start, Adolf Stop, Adolf Pause, Adolf Resume), entered a 2 second delay off on each switch and put some LUA code into work. Then I made the following LUA script:[...]
Whats do you mean? I created 5 virtual devices, text type, and I named them: Vicky Start, Vicky Stop, Vicky Pause, Vicky Resume.
Then..Do I have to put a lua script inside each device? I am confused.....I am a noob sorry.
BakSeeDaa wrote: Thursday 23 February 2017 11:28

Code: Select all

var domoTextDeviceIdx = 503; // Change this
I changed Idx value putting value "5" that is the value of Dummy Hardware IDX but it is also the value of the first Virtual DEvice tha is called Vicky_Pause.
When Roomba start I can see the Vicky_Pause status changing (from charge to run to dock to pause...). But it is not supposed to work in this way.
I am missing something....

Can you help me?

Thanks

Xavier
nini57
Posts: 1
Joined: Thursday 17 October 2019 23:58
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by nini57 »

hello,
when I launch my script receive this

iRobot Phase same as before = stop
Received command: start

but the robot don't start
you know why ?
User avatar
bewo
Posts: 74
Joined: Monday 13 July 2015 12:27
Target OS: Linux
Domoticz version: 2021.1
Location: Bavaria - Germany
Contact:

Re: iRobot Roomba 980 integration

Post by bewo »

Hi guys...!

Thank's for the idea's, your scripts and the inspiration... I've did it in a different way.
The Rest API which offered beside dorita980 by koalazak (linked above) is updated and works.
So i thougt this an nicer way...

In my house are working two roboters. So i've set up an instance of the restAPI for each robot. Then i use two lua scripts. One time triggered for status updating and the other one device triggered to control the robots.

My comments are german. :-) So if there's somebody who want to use, or there is a question -> just ask, i will do my best @translating. ;)


This are the control switches:
switches.png
switches.png (43.65 KiB) Viewed 8073 times

This are the sensor devices:
devices.png
devices.png (89.24 KiB) Viewed 8073 times

This is the status script (time triggered):

Code: Select all

-- Skript zum Prüfen des Status eines Staubsauger-Roboter iRobot Roomba
-- --------------------------------------------------------------------
-- Skript funktioniert nur im Zusammenspiel mit zweiten Steuer-Skript:
-- Der Roboter kann auch per Tastendruck am Roboter selbst gesteuert werden. Deshalb muss jeweils unterschieden
-- werden zwischen tatsächlichem Schaltbefehl, und reinem Status setzen. Dieses Skript ruft über die App-Schnittstelle
-- des Roboters eine JSON-Datei ab und setzt diese wenn notwendig in Domoticz-Uservariablen.

-- Skriptanforderungen und Voraussetzungen siehe ganz unten!

-- Domoticz-JSON-Bibliothek laden (notwendig zum Parsen der JSON-Datei):
json = (loadfile "/opt/domoticz/scripts/lua/JSON.lua")()

commandArray = {}

-- Hier die einzelnen Roboter bestimmen:
Roboter = {

    Erdgeschoss = { Name = 'Goofy', API_URL = 'http://localhost:3000/api/local' },
    Obergeschoss = { Name = 'Pluto', API_URL = 'http://localhost:3001/api/local' }
}


-- Und los:
for Roboter,Roboter in pairs(Roboter) do

    -- Da die Roboter mit WLAN arbeiten, erfolgt alles unter der Voraussetzung, dass WLAN an ist.
    if otherdevices['WLAN-Name'] == 'On' then
        
        -- #############################################################################################################
        
        -- Informationen vom Roboter abrufen und in LUA-Tabelle schreiben:
        json_abruf=assert(io.popen('curl '..Roboter.API_URL..'/info/mission'))
        json_Roboter_Daten = json_abruf:read('*all')
        json_abruf:close()
        Roboter_Daten = json:decode(json_Roboter_Daten)
        
        -- Zuordnung der einzelnen Infos:
            Art_Mission = Roboter_Daten.cleanMissionStatus.cycle            -- Art der Misson
            Status = Roboter_Daten.cleanMissionStatus.phase                 -- Aktueller Status
            --Ablaufzeit = Roboter_Daten.cleanMissionStatus.expireM         -- Zeit bis die Mission abgelaufen ist, also in der die Mission wieder aufgenommen werden könnte
            --Restakkuzeit = Roboter_Daten.cleanMissionStatus.rechrgM       -- Betriebszeit bis zu leerem Akku 
            Fehler = Roboter_Daten.cleanMissionStatus.error                 -- Fehler vorhanden, gibt eine Nummer aus
            Nicht_Bereit = Roboter_Daten.cleanMissionStatus.notReady        -- Status "nicht bereit", gibt auch eine Nummer aus
            --Missions_Dauer = Roboter_Daten.cleanMissionStatus.mssnM       -- Bisherige Dauer der Mission
            --Flaeche = Roboter_Daten.cleanMissionStatus.sqft               -- Gereinigte Fläche in der aktuellen Mission
            --Ausloeser = Roboter_Daten.cleanMissionStatus.initiator        -- Auslöser der aktuellen Mission
            --Missions_Nummer = Roboter_Daten.cleanMissionStatus.nMssn      -- Nummer der aktuellen Mission
            Schmutzbehaelter_vorhanden = tostring(Roboter_Daten.bin.present)    -- Schmutzbehälter vorhanden? Ja/Nein
            Schmutzbehaelter_voll = tostring(Roboter_Daten.bin.full)        -- Schmutzbehälter voll? Ja/Nein
            Batterieprozent = Roboter_Daten.batPct                          -- Aktueller Batteriestand in Prozent (Zehnerstufen)
        
        
    -- #############################################################################################################
        
        -- Domoticz-Nutzervariablen aktualisieren, falls notwendig:
        
        -- Art der Misson in Nutzervariable schreiben:
        if Art_Mission ~= uservariables[Roboter.Name..'_JSON_Art_Mission'] then
            commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_JSON_Art_Mission'] = Art_Mission}
            print('Staubsauger-Roboter: '..Roboter.Name..' -> Art der Misson auf '..Art_Mission..' aktualisiert.')
        end
        
        -- Status in Nutzervariable schreiben:
        if Status ~= uservariables[Roboter.Name..'_JSON_Status'] then
            commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_JSON_Status'] = Status}
            print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf '..Status..' aktualisiert.')
        end
        
    -- #############################################################################################################
        
        -- Störungen setzen und Meldung verschicken:
        
        if Fehler > 0 or (Nicht_Bereit > 0 and Nicht_Bereit < 15) and otherdevices[Roboter.Name] ~= 'Störung' and 
            Schmutzbehaelter_vorhanden == 'true' then
                commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 20'} -- Level 20 = Störung
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Achtung! Fehler-Nr.: '..Fehler..' ist vorhanden.')
                if uservariables[Roboter.Name..'_Nachrichtenstatus'] ~= 'Versendet: Störung' then
                    commandArray[#commandArray+1] = {['SendNotification'] = string.upper(Roboter.Name)..'#Achtung, der Staubsauger-Roboter '..Roboter.Name..' meldet eine Störung. Bitte prüfen!'}
                    commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Versendet: Störung'} -- Level 20 = Störung
                end
                
        elseif Nicht_Bereit == 15 then
            if Batterieprozent <= 20 and otherdevices[Roboter.Name] ~= 'Störung' then
                commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 20'} -- Level 20 = Störung
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Achtung Akku leer!')
                if string.sub(uservariables[Roboter.Name..'_Nachrichtenstatus'], 1, 9) ~= 'Versendet' then
                    commandArray[#commandArray+1] = {['SendNotification'] = string.upper(Roboter.Name)..'#Achtung, der Akku von Staubsauger-Roboter '..Roboter.Name..' ist komplett leer. Bitte manuell zum Aufladen ins Dock bringen!'}
                    commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Versendet: Akku leer'}
                end
            elseif Batterieprozent > 20 and otherdevices[Roboter.Name] ~= 'Aufladen' and Art_Mission == 'none' and Status == 'charge' then
                commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 0'} -- Level 0 = Aufladen
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Akku war komplett leer, aktuell läuft der Ladevorgang!')
                if string.sub(uservariables[Roboter.Name..'_Nachrichtenstatus'], 1, 9) ~= 'Versendet' then
                    commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Bereit'}
                end    
            end    
        end
        
        -- Störung wieder freigeben:
        if Fehler == 0 and Nicht_Bereit == 0 then
            if uservariables[Roboter.Name..'_Nachrichtenstatus'] == 'Versendet: Störung' then
                commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Bereit'}
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Nachrichtenstatus zurück gesetzt.')
            end
        end
        
        
    -- #############################################################################################################
        
        -- Schmutzbehälter-Service (Anzeige von Nachricht und Symbol)
        --
        -- Damit ein Hinweis auf einen (teils) gefüllten Schmutzbehälter gegeben werden kann, wird die Laufzeit des
        -- Roboters gezählt (im Zeitzähler-Skript) und unterstellt, dass sich der Schmutzbehälter mit der Zeit füllt.
        -- Bei Laufzeit von 90 min. und nicht geleertem Behälter wird ein Hinweis verschickt.
        
        schmutzbehaelter_device = Roboter.Name..' Status Schmutzbehälter'
        
        -- Status auf OK setzen und Laufzeit zurück setzen
        if Schmutzbehaelter_vorhanden == 'true' then
            
            if uservariables[Roboter.Name..'_Laufzeit'] <= 45 then
                textstatus = 'Behälter ist leer. Alles OK!'
                if otherdevices_svalues[schmutzbehaelter_device] ~= textstatus then
                    commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[schmutzbehaelter_device]..'|1|'..textstatus} -- 1 = grün
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status Schmutzbehälter aktualisiert.')
                end
                -- Versandmeldung zurück setzen
                if uservariables[Roboter.Name..'_Nachrichtenstatus'] == 'Versendet: Behälter leeren' then
                    commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Bereit'}
                end
                
            -- Ändern des Textes, nachdem Goofy schon ein wenig gelaufen ist.
            elseif uservariables[Roboter.Name..'_Laufzeit'] > 45 and uservariables[Roboter.Name..'_Laufzeit'] < 90 then
                textstatus = 'Behälter ist schmutzig, aber nicht voll.'
                if otherdevices_svalues[schmutzbehaelter_device] ~= textstatus then
                    commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[schmutzbehaelter_device]..'|2|'..textstatus} -- 2 = gelb
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status Schmutzbehälter aktualisiert.')
                end
                    
            -- Hinweis bei vermutet gefülltem Behälter
            elseif uservariables[Roboter.Name..'_Laufzeit'] >= 90 then
                textstatus = 'Behälter ist vermutlich voll. Bitte leeren!'
                if otherdevices_svalues[schmutzbehaelter_device] ~= textstatus then
                    commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[schmutzbehaelter_device]..'|3|'..textstatus} -- 3 = orange
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status Schmutzbehälter aktualisiert.')
                end    
                if string.sub(uservariables[Roboter.Name..'_Nachrichtenstatus'], 1, 9) ~= 'Versendet' then
                    commandArray[#commandArray+1] = {['SendNotification'] = string.upper(Roboter.Name)..'#Der Schmutzbehälter von Staubsauger-Roboter '..Roboter.Name..' sollte gefüllt sein. Bitte demnächst leeren!'}
                    commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Versendet: Behälter leeren' }
                end
            end
            
        else
            -- Behälter wurde raus genommen -> Status ändern und Laufzeit zurück setzen:
            textstatus = 'Behälter ist nicht eingesetzt.'
            if otherdevices[schmutzbehaelter_device] ~= textstatus then
                commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[schmutzbehaelter_device]..'|0|'..textstatus} -- 0 = grau
                commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Laufzeit'] = tostring(0) }
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Status Schmutzbehälter aktualisiert.')
            end
        end
        
        
        -- Hinweis & Co. bei wirklich vollem Schmutzbehälter (Der Roboter meldet selbst voll!):
        if Schmutzbehaelter_voll == 'true' and otherdevices[Roboter.Name] ~= 'Störung' then
            textstatus = 'Behälter ist VOLL. Muss zwingend geleert werden!'
            if otherdevices_svalues[schmutzbehaelter_device] ~= textstatus then
                commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[schmutzbehaelter_device]..'|4|'..textstatus} -- 3 = rot
                commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 20' } -- Level 20 = Störung
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Status Schmutzbehälter aktualisiert.')
            end 
            if string.sub(uservariables[Roboter.Name..'_Nachrichtenstatus'], 1, 9) ~= 'Versendet' then
                print(Roboter.Name..': Achtung der Schmutzbehälter ist voll! Muss geleert werden!')
                commandArray[#commandArray+1] = {['SendNotification'] = string.upper(Roboter.Name)..'#Achtung der Schmutzbehälter von '..Roboter.Name..' ist voll! Muss zwingend geleert werden!'}
                commandArray[#commandArray+1] = {['Variable:'..Roboter.Name..'_Nachrichtenstatus'] = 'Versendet: Behälter leeren' }
            end
        end
        
    -- #############################################################################################################
        
        -- Batteriestatus (Symbol und Stand) aktualisieren:
        
        batterie_device = Roboter.Name..' Batteriestand'
        
        -- Passends Batterie-Icon festlegen:
        if Batterieprozent <= 20 then
            batteriesymbol = '102'
        elseif Batterieprozent > 20 and Batterieprozent <= 50 then
            batteriesymbol = '104'
        elseif Batterieprozent > 50 and Batterieprozent <= 90 then
            batteriesymbol = '101'
        elseif Batterieprozent > 90 then
            batteriesymbol = '103'
        end
        
        -- Batteriestand schreiben
        if Batterieprozent ~= tonumber(otherdevices_svalues[batterie_device]) then
            commandArray[#commandArray+1] = {['UpdateDevice'] = otherdevices_idx[batterie_device]..'|0|'..Batterieprozent}
            print('Staubsauger-Roboter: '..Roboter.Name..' -> Batteriestand auf '..Batterieprozent..'% aktualisiert.')
            os.execute('curl "http://localhost:8077/json.htm?type=setused&idx='..otherdevices_idx[batterie_device]..'&name='..Roboter.Name..'%20Batteriestand&description=&switchtype=0&customimage='..batteriesymbol..'&devoptions=1;%25&used=true"')
        end
        
        
        -- Roboter in's Dock holen, wenn die Batterie leer wird:
        if Batterieprozent <= 30 and Status ~= 'hmUsrDock' then
            commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 10' } -- Level 10 = Dock
            print('Staubsauger-Roboter: '..Roboter.Name..' -> Akkustand geht bald zu Ende. Modus auf Dock gestellt.')
        end
        
    -- #############################################################################################################
        
        -- Status des Roboter-Wahlschalters aktualisieren, falls manuell am Roboter gedrückt wurde:
        
        if Fehler == 0 and Nicht_Bereit == 0 then
                
            -- Roboter ist auf dem Weg in's Dock:
            if (Art_Mission == 'clean' or Art_Mission == 'dock') and Status == 'hmUsrDock' and
                otherdevices[Roboter.Name] ~= 'Dock' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 10' } -- Level 10 = Dock
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Dock aktualisiert.')

            -- Roboter ist schon im Dock, aber der Akku noch leer:
            elseif Art_Mission == 'none' and Status == 'charge' and Batterieprozent <= 80 and
                otherdevices[Roboter.Name] ~= 'Aufladen' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 0' } -- Level 0 = Aufladen
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Aufladen aktualisiert.')
                    
            -- Roboter ist schon im Dock, und der Akku ausreichend geladen:
            elseif Art_Mission == 'none' and Status == 'charge' and Batterieprozent > 80 and
                otherdevices[Roboter.Name] ~= 'Dock' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 10' } -- Level 10 = Dock
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Dock aktualisiert.')
                    
            -- Roboter ist außerhalb des Docks und nicht aktiv, bzw. Reinigungsmission wurde pausiert:
            elseif (Art_Mission == 'none' or Art_Mission == 'clean') and Status == 'stop' and
                otherdevices[Roboter.Name] ~= 'Pause' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 30' } -- Level 30 = Pause
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Pause aktualisiert.')
                    
            -- Roboter ist im Spotbetrieb:
            elseif Art_Mission == 'spot' and Status == 'run' and
                otherdevices[Roboter.Name] ~= 'Saugen' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 40' } -- Level 40 = Saugen
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Saugen aktualisiert.')
                
            -- Roboter ist im normalen Saugbetrieb:
            elseif Art_Mission == 'clean' and (Status == 'run' or Status == 'hmPostMsn') and
                otherdevices[Roboter.Name] ~= 'Saugen' then
                    commandArray[#commandArray+1] = {[Roboter.Name] = 'Set Level 40' } -- Level 40 = Saugen
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Status auf Saugen aktualisiert.')
            end
            
        end
    end
    

    -- Batteriestatus aktiv halten (ohne WLAN, oder bei fehlender Veränderung)
    if os.date('*t').min == 15 then
        Batterieprozent = tonumber(otherdevices_svalues[Roboter.Name..' Batteriestand'])
        commandArray[#commandArray+1]={['UpdateDevice'] = otherdevices_idx[Roboter.Name..' Batteriestand']..'|0|'..Batterieprozent}
        print('Staubsauger-Roboter: '..Roboter.Name..' -> Batteriestand aktiv gehalten.')
    end
 
end    



return commandArray


--[[

Hinweise und Erklärungen:
-------------------------
-> Bei den einzelnen Geräten sind jeweils der Robotername und die Rest-API-URL zu setzen!
Folgende Variabeln / Schalter usw. müssen pro Roboter angelegt sein:
- Wahlschalter mit folgenden Stufen: Aufladen, Dock, Störung, Pause, Saugen
- Sensor-Device für Batteriestand (Custom Sensor) mit Namen *Robotername* Batteriestand 
- Text-Device für Schmutzbehälter-Status mit Namen *Robotername* Status Schmutzbehälter
- Zeitzähler-Device für die Gesamtlaufzeit (wenn gewünscht)

Folgende Nutzervariablen müssen angelegt sein:
- *Robotername*_JSON_Art_Mission
- *Robotername*_JSON_Status
- *Robotername*_Nachrichtenstatus
- *Robotername*_Laufzeit

*Robotername* ist dabei immer entsprechend anzupassen!
Beispiel, der Roboter heißt Goofy. Dann würde eine Variable Goofy_JSON_Art_Mission heißen.

--]]

And this is my script for controlling (device triggered):

Code: Select all

-- Skript zum Steuern von Staubsauger-Roboteren des Typs iRobot Roomba
-- -------------------------------------------------------------------
-- Skript funktioniert nur im Zusammenspiel mit zweiten Status-Skript!
-- Der Roboter kann auch per Tastendruck am Roboter selbst gesteuert werden. Deshalb muss jeweils unterschieden
-- werden zwischen tatsächlichem Schaltbefehl, und reinem Status setzen. Dieses Skript schickt die tatsächlichen
-- Schaltbefehle, das zweite Skript aktualsiert minütlich und setzt die entsprechenden Stati, wenn notwendig.

commandArray = {}

-- Laden der eigenen Funktions-Sammlung
dofile("/opt/domoticz/scripts/lua/Eigene_Funktionen.lua")

-- Hier die einzelnen Roboter bestimmen:
Roboter = {

    Erdgeschoss = { Name = 'Goofy', API_URL = 'http://localhost:3000/api/local' },
    Obergeschoss = { Name = 'Pluto', API_URL = 'http://localhost:3001/api/local' }
}


-- Und los:
for Roboter,Roboter in pairs(Roboter) do
    
    var_Mission = tostring(Roboter.Name..'_JSON_Art_Mission')
    var_Status = Roboter.Name..'_JSON_Status'
    Batteriestand = tonumber(otherdevices_svalues[Roboter.Name..' Batteriestand'])
    
    -- Da der Roboter mit WLAN arbeitet, erfolgt alles unter der Voraussetzung, dass WLAN an ist.
    if otherdevices['WLAN-Name'] == 'On' then
            
        ---------------------------------------------------------------------------------------------------------
        -- Starten des Dockvorgang
        if devicechanged[Roboter.Name] == 'Dock' then
            -- Aus dem aktiven Reinigungsbetrieb in's Dock holen:
            if uservariables[var_Mission] == 'clean' and uservariables[var_Status] == 'run' then
                os.execute ('curl '..Roboter.API_URL..'/action/pause && sleep 1 && curl '..Roboter.API_URL..'/action/dock')
                commandArray['Variable:'..var_Status] = 'hmUsrDock'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Befehl zum in das Dock-Fahren verschickt!')
            -- Aus der Pause in's Dock holen:
            elseif uservariables[var_Status] == 'stop' then
                os.execute ('curl '..Roboter.API_URL..'/action/dock')
                commandArray['Variable:'..var_Status] = 'hmUsrDock'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Befehl zum in das Dock-Fahren verschickt!')
            end    
        end
            
        ---------------------------------------------------------------------------------------------------------
        -- Pausieren des Reinigungsvorgangs (im normalen Reinigungsbetrieb oder auf dem Weg zum Dock)
        if devicechanged[Roboter.Name] == 'Pause' and
            (uservariables[var_Mission] == 'clean' or uservariables[var_Mission] == 'dock') and
            (uservariables[var_Status] == 'run' or uservariables[var_Status] == 'hmUsrDock') then
                os.execute ('curl '..Roboter.API_URL..'/action/pause')
                commandArray['Variable:'..var_Status] = 'stop'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Aktiver Vorgang wurde pausiert!')
        end
            
        ---------------------------------------------------------------------------------------------------------
        -- Starten des Reinigungsvorgangs
        if devicechanged[Roboter.Name] == 'Saugen' then
            -- aus Ladestation oder unterwegs
            if uservariables[var_Mission] == 'none' and
                (uservariables[var_Status] == 'charge' or uservariables[var_Status] == 'stop') then
                os.execute ('curl '..Roboter.API_URL..'/action/start')
                commandArray['Variable:'..var_Mission] = 'clean'
                commandArray['Variable:'..var_Status] = 'run'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Saugmission gestartet!')
            -- weiter machen nach Störung
            elseif uservariables[var_Mission] == 'clean' and uservariables[var_Status] == 'stuck' then
                os.execute ('curl '..Roboter.API_URL..'/action/resume')
                commandArray['Variable:'..var_Mission] = 'clean'
                commandArray['Variable:'..var_Status] = 'run'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Saugmission wieder aufgenommen!')
            -- weiter machen nach Pause
            elseif uservariables[var_Mission] == 'clean' and uservariables[var_Status] == 'stop' then
                os.execute ('curl '..Roboter.API_URL..'/action/resume')
                commandArray['Variable:'..var_Mission] = 'clean'
                commandArray['Variable:'..var_Status] = 'run'
                print('Staubsauger-Roboter: '..Roboter.Name..' -> Saugmission wieder aufgenommen!')
            -- in normale Reinigung wechseln nach 10 Minuten Spot-Modus
            elseif uservariables[var_Mission] == 'spot' and uservariables[var_Status] == 'run' and
                timedifference(otherdevices_lastupdate[Roboter.Name]) > 600 then
                    os.execute ('curl '..Roboter.API_URL..'/action/pause && sleep 1 && curl '..Roboter.API_URL..'/action/start')
                    commandArray['Variable:'..var_Mission] = 'clean'
                    commandArray['Variable:'..var_Status] = 'run'
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Saugmission gestartet (nach Spot-Modus)!')
            -- in normale Reinigung wechseln bei diesem selstamen hmPostMsn-Modus
                -- und momentan auch Benachrichtigung schicken um das zu prüfen
            elseif uservariables[var_Mission] == 'clean' and uservariables[var_Status] == 'hmPostMsn' and
                Batteriestand > 30 then
                    os.execute ('curl '..Roboter.API_URL..'/action/pause && sleep 1 && curl '..Roboter.API_URL..'/action/start')
                    commandArray['Variable:'..var_Mission] = 'clean'
                    commandArray['Variable:'..var_Status] = 'run'
                    print('Staubsauger-Roboter: '..Roboter.Name..' -> Saugmission gestartet (nach hmPostMsn)!')        
            end
        end
        
    end

end


return commandArray




Best regards and a Happy New Year together! :D
Last edited by bewo on Monday 13 January 2020 12:51, edited 1 time in total.
Individual projects:
Domoticz on a Intel Xeon Server | AeonLabs Z-Wave Gen.5 | RFXCOM RFXtrx433E USB | ESP-Wifi-Modules | Shellys
Wall-mounted 22" Touch Control Display (self construct) | LUA wind monitor| LUA heating control | and many many more :)
Vondee
Posts: 30
Joined: Sunday 12 January 2020 19:06
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by Vondee »

Hi,
Great post!

I followed through all the guides and were able to get the rest980 working and communicating with my iRobot 960. I also used the scripts as provided (tweaked some directories / language) and was working!
I also was able to run the mapping (Localhost:3000/map) and worked well.

Currently I face the problem that the rest980 stops communicating after a couple of minutes. I see error logs in Domoticz when this happens and when I try to open the mapping then there is no connection. When I do a restart (nmp start) everting is workin again, but sagain stops after a couple of minutes.

Any idea what is going wrong?

Thanks

Wim
User avatar
bewo
Posts: 74
Joined: Monday 13 July 2015 12:27
Target OS: Linux
Domoticz version: 2021.1
Location: Bavaria - Germany
Contact:

Re: iRobot Roomba 980 integration

Post by bewo »

Hi Wim,

welcome in the forum!
In which way do you start the node?
Individual projects:
Domoticz on a Intel Xeon Server | AeonLabs Z-Wave Gen.5 | RFXCOM RFXtrx433E USB | ESP-Wifi-Modules | Shellys
Wall-mounted 22" Touch Control Display (self construct) | LUA wind monitor| LUA heating control | and many many more :)
Vondee
Posts: 30
Joined: Sunday 12 January 2020 19:06
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by Vondee »

Hi,

I managed to get rest980 to run as a service and is working now.

Where did you get the nice icons for the robot and battery?
User avatar
bewo
Posts: 74
Joined: Monday 13 July 2015 12:27
Target OS: Linux
Domoticz version: 2021.1
Location: Bavaria - Germany
Contact:

Re: iRobot Roomba 980 integration

Post by bewo »

The robot icon i've created myself, the battery icons are from the BatteryLevel-Plugin (and @logread got it from wpclipart.com), so there's no license, the icon can be used.. There they are:
batterylevelfull icons.zip
(7.17 KiB) Downloaded 138 times
batterylevellow icons.zip
(6.15 KiB) Downloaded 176 times
batterylevelok icons.zip
(6.61 KiB) Downloaded 142 times
Last edited by bewo on Friday 17 January 2020 12:43, edited 1 time in total.
Individual projects:
Domoticz on a Intel Xeon Server | AeonLabs Z-Wave Gen.5 | RFXCOM RFXtrx433E USB | ESP-Wifi-Modules | Shellys
Wall-mounted 22" Touch Control Display (self construct) | LUA wind monitor| LUA heating control | and many many more :)
User avatar
bewo
Posts: 74
Joined: Monday 13 July 2015 12:27
Target OS: Linux
Domoticz version: 2021.1
Location: Bavaria - Germany
Contact:

Re: iRobot Roomba 980 integration

Post by bewo »

batterylevelempty icons.zip
(5.29 KiB) Downloaded 137 times
Saugroboter-mit-Ladesymbol.zip
(18.12 KiB) Downloaded 170 times
Individual projects:
Domoticz on a Intel Xeon Server | AeonLabs Z-Wave Gen.5 | RFXCOM RFXtrx433E USB | ESP-Wifi-Modules | Shellys
Wall-mounted 22" Touch Control Display (self construct) | LUA wind monitor| LUA heating control | and many many more :)
Vondee
Posts: 30
Joined: Sunday 12 January 2020 19:06
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by Vondee »

Vielen Dank!!! :D :D
Vondee
Posts: 30
Joined: Sunday 12 January 2020 19:06
Target OS: Raspberry Pi / ODroid
Domoticz version:
Contact:

Re: iRobot Roomba 980 integration

Post by Vondee »

The run time does not work, could it be that the script to calculate the ‘_Laufzeit’ is missing?
Many thanks again!
User avatar
bewo
Posts: 74
Joined: Monday 13 July 2015 12:27
Target OS: Linux
Domoticz version: 2021.1
Location: Bavaria - Germany
Contact:

Re: iRobot Roomba 980 integration

Post by bewo »

Hi Vondee,

yes that could be. ;-)

If you copied my script 1:1, just add this lines above the "end" of the for loop:

Code: Select all

-- Count the working time
if otherdevices[Roboter.Name] == 'Saugen' then
    working_time = tonumber(uservariables[Roboter.Name..'_Laufzeit']) + 1
    commandArray[#commandArray+1]={['Variable:'..Roboter.Name..'_Laufzeit'] = tostring(working_time)}
end
Individual projects:
Domoticz on a Intel Xeon Server | AeonLabs Z-Wave Gen.5 | RFXCOM RFXtrx433E USB | ESP-Wifi-Modules | Shellys
Wall-mounted 22" Touch Control Display (self construct) | LUA wind monitor| LUA heating control | and many many more :)
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest