Looking for an automatable vacuum cleaner

I looked around quite some time and it seems, that there is no vacuum cleaner I can integrate with openhab. My main usecase would be to start the cleaning when everybody left the house. Presence detection working very well (with the fritzbox-binding), so I thought this would be an easy use case.

Any ideas why there is no such product and wether we can expect something in the near future?

Have a look at RooWifi: http://www.roowifi.com/

Here is an integration with Vera: https://github.com/undert03/vera-roomba

You could most likely use the HTTP binding to get status and send it commands.

I am using it with a LG Hombot.
http://www.roboter-forum.com/showthread.php?10009-LG-Hombot-3-0-WLAN-Steuerung-per-Weboberflļæ½che
I am very pleased with it. Especially the dual eye is awesome!

Just did research yesterday on that, too, outcome was exactly those two options (RooWifi for Roombas, LG Hombot). Plus thereā€™s the Roomba 980 to have native WiFi now, but itā€™s way too expensive.
I chose to go for a Hombot because it has a hackable Linux firmware, thus itā€™s potentially much more flexible than the Roombas.Any recent model should do. I ordered a VR64607 on amazon.es for 320ā‚¬, now anxiously waiting for it to arrive ā€¦
@Spaceman_Spiff: care to share your openHAB config for Hombots ?

PS: a Fritz!box to do presence detection ?

Thanks, these recommendations help a lot!
I think Iā€™ll got with a roomba 880 and the roowifi. They tend to have the best long-time customer satisfaction.

The Roomba 980 is not accessible from the outside, is it? I think it works just with the app.

Regarding the fritz!box, this is it: https://github.com/openhab/openhab/wiki/FritzBox-TR064-Binding

Well, depends on who youā€™re asking. Thereā€™s probably more opinions than experiences.

So do I. iRobot is not famous for being ā€˜openā€™. AFAIK they donā€™t even provide firmware updates (except for the expensive 9xx series), they want you to buy a better model.
That was the major argument for me to go with a Hombot (yes, even if you donā€™t root it, LG provides updates).

Ah, MAC detection. Ok, Iā€™m using network health binding to ping IPs instead (fixed IP assignment on Fritz!box).

As requested here are my rules:

//-----------------------------------------------------------------------------------------------------------------------------------------------------------
// Luigi
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
Switch    Luigi_Do_Start                    "Reinigung starten"                     { autoupdate="false"}
Switch    Luigi_Do_Charge                    "Ladestation suchen"                    { autoupdate="false"}

Switch    Luigi_UpdateStatus                "Update Status"
String    Luigi_Status                    "Status [%s]"                                (gMyOpenHAB)
String    Luigi_Batterie                    "Batterie [%s]"
String    Luigi_LastClean                    "Letzte Reinigung [%s]"
Number    Luigi_LastClean_Dauer            "Dauer letzte Reinigung [%.1f min]"            (gInitializeZero)

Number    Luigi_Fade_Anzahl                "Luigi Fade Anzahl [%d]"        (gInitializeZero)

Switch    cWohnzimmerLuigiAutoStart        "Automatische Reinigung"                        (gInitializeOn,gConfig)
Switch    cWohnzimmerLuigiStartAbwesend    "Automatische Reinigung nur bei Abwesenheit"    (gInitializeOn,gConfig)
Number    cWohnzimmerLuigiAutoStartTime    "Start der Reinigung [%d Uhr]"                    (gInitializeZero)

With the following rules I get the current status and also do the scheduling:

// Imports
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

import org.joda.time.*

import java.util.regex.Matcher
import java.util.regex.Pattern

import java.util.Date
import java.text.SimpleDateFormat 

//Global Variables

var Number ReinigungStart = 0

var boolean ReinigungsAusfuehren    = false

//Luigi-Status Ć¼ber das Netzwerk aktualisieren
rule "Luigi update Status"
when
    Item Luigi_UpdateStatus received update ON
then
    val String status_raw = sendHttpGetRequest( "http://192.168.0.10:6260/status.html")
    if( status_raw == null) {
        logWarn(    "Wohnzimmer", String::format( "%15s | offline!", "Luigi"))
        Luigi_Status.postUpdate( "Offline")
        Luigi_Batterie.postUpdate( "Offline")
        return false
    }
    
    
    //logDebug(    "Wohnzimmer", String::format( "%15s | http:\n%s\n", "Luigi", status_raw))

    val Pattern pattern_status        = Pattern::compile( ".*<b>Robot-state</b>: <status>(.+)</status>.*")
    val Pattern pattern_batterie    = Pattern::compile( ".*<b>Batt-Perc</b>: <batterie>(.+)</batterie>.*")
    val Pattern pattern_reinigung    = Pattern::compile( ".*<b>Last-Clean</b>: <lastclean>(\\d+)/(\\d+)/(\\d+)/(\\d+)/(\\d+).+</lastclean>.*")

    var Matcher matcher_status        = pattern_status.matcher( status_raw)
    var Matcher matcher_batterie    = pattern_batterie.matcher( status_raw)
    var Matcher matcher_reinigung    = pattern_reinigung.matcher( status_raw)

    matcher_status.find()
    matcher_batterie.find()
    matcher_reinigung.find()

    val int count_status    = matcher_status.groupCount()
    val int count_batterie    = matcher_batterie.groupCount()
    val int count_reinigung    = matcher_reinigung.groupCount()

    var boolean ok = true;

    if( count_status != 1) { 
        logWarn(    "Wohnzimmer", String::format( "%15s | Groupcount status doesn't match! (%i)", "Luigi", count_status))
        ok = false
    }
    
    if( count_batterie    != 1) { 
        logWarn(    "Wohnzimmer", String::format( "%15s | Groupcount batterie doesn't match! (%i)", "Luigi", count_batterie))
        ok = false    
    }
    
    if( count_reinigung    != 5) {
        logWarn(    "Wohnzimmer", String::format( "%15s | Groupcount reinigung doesn't match! (%i)", "Luigi", count_reinigung))
        ok = false
    }

    
    var boolean request_refresh = true
    if( ok) {
        val String neu_status        = matcher_status.group(1)
        val String neu_batterie        = matcher_batterie.group(1) + " %"
        val String neu_reinigung    = String::format( "%s.%s.%s %s:%s", matcher_reinigung.group(3), matcher_reinigung.group(2), matcher_reinigung.group(1), matcher_reinigung.group(4), matcher_reinigung.group(5))
    
        logInfo(    "Wohnzimmer", String::format( "%15s |    Status: %s", "Luigi", neu_status))
        logInfo(    "Wohnzimmer", String::format( "%15s |  Batterie: %s", "Luigi", neu_batterie))
        logInfo(    "Wohnzimmer", String::format( "%15s | Reinigung: %s", "Luigi", neu_reinigung))

        if( Luigi_Status.state != neu_status)        Luigi_Status.postUpdate(neu_status)
        if( Luigi_Batterie.state != neu_status)        Luigi_Batterie.postUpdate(neu_batterie)
        if( Luigi_LastClean.state != neu_status)    Luigi_LastClean.postUpdate(neu_reinigung)
        
        //Dauer live berechnen (in min)
        if( ReinigungStart != 0) {
            val Number dauer = (DateTimeUtils::currentTimeMillis() - ReinigungStart) / 60000
            logInfo(    "Wohnzimmer", String::format( "%15s |     Dauer: %.1f min", "Luigi", dauer))
            if( Luigi_LastClean_Dauer.state != dauer)    Luigi_LastClean_Dauer.postUpdate( dauer)
        }

        if( neu_status == "CHARGING") {
            request_refresh    = false
            ReinigungStart    = 0
        }
    }
    else {
        //Ausgabe der http-Antwort
        logWarn(    "Wohnzimmer", String::format( "%15s | http:\n%s\n", "Luigi", status_raw))
    }
    
    //Wenn wir saugen ƶfter anfragen
    if( request_refresh) {
        createTimer( now.plusSeconds(30)) [|
            Luigi_UpdateStatus.postUpdate(ON)
        ]
    }
    
    return false
end

rule "Luigi Pushover notifications"
when
    Item Luigi_Status changed
then
    val String ist = Luigi_Status.state.toString()
    val String war = previousState.toString()
    
    
    if( ist == "Offline" && war != "Offline") {
        var String output = String::format( "[%1$td.%1$tm %1$tH:%1$tM:%1$tS]: Luigi offline!", new Date())
        pushover( output, 1)
        return false
    }
    
    if( ist != "Offline" && war == "Offline") {
        var String output = String::format( "[%1$td.%1$tm %1$tH:%1$tM:%1$tS]: Luigi back online!", new Date())
        pushover( output, 0)
        return false
    }
end

rule "Luigi starten"
when
    Item Luigi_Do_Start received command ON
then
    ReinigungStart = DateTimeUtils::currentTimeMillis()

    var String output = String::format( "[%1$td.%1$tm %1$tH:%1$tM:%1$tS]: Luigi automatische Reinigung start!", new Date())
    pushover( output, 0)
    
    logInfo(    "Wohnzimmer", String::format( "%15s | Reinigung startet!", "Luigi"))
    val String http_return = sendHttpGetRequest( "http://192.168.0.10:6260/json.cgi?%7B%22COMMAND%22%3A%22CLEAN_START%22%7D")
    logDebug(    "Wohnzimmer", String::format( "%15s | http:\n%s\n", "Luigi", http_return))

    createTimer( now.plusSeconds(10)) [|
        Luigi_UpdateStatus.postUpdate(ON)
    ]

end

rule "Luigi Ladestation"
when
    Item Luigi_Do_Charge received command ON
then
    logInfo(    "Wohnzimmer", String::format( "%15s | Ladestation wird gesucht!", "Luigi"))
    val String http_return = sendHttpGetRequest( "http://192.168.0.10:6260/json.cgi?%7b%22COMMAND%22:%22HOMING%22%7d")
    logDebug(    "Wohnzimmer", String::format( "%15s | http:\n%s\n", "Luigi", http_return))
end

rule "Luigi Startzeit Change"
when
    Item cWohnzimmerLuigiAutoStartTime changed
then
    //Damit das Autoinitialize geht
    val Number startzeit = cWohnzimmerLuigiAutoStartTime.state as DecimalType
    if( startzeit < 1) {
        cWohnzimmerLuigiAutoStartTime.postUpdate( 11)
        return false
    }
    
    logInfo(    "Wohnzimmer", String::format( "%15s | Automatische Reinigung um %.0f Uhr", "Luigi", startzeit.floatValue()))
end

rule "Luigi Automatische Reinigung"
when
    Time cron "0 0 * * * ?"
then
    Luigi_UpdateStatus.postUpdate(ON)

    if( cWohnzimmerLuigiAutoStart.state != ON) return false

    if( now.getHourOfDay() == cWohnzimmerLuigiAutoStartTime.state as DecimalType) {
        ReinigungsAusfuehren = true
        logDebug(    "Wohnzimmer", String::format( "%15s | Reinigungswunsch", "Luigi"))
    }
    
    if( ReinigungsAusfuehren) {
        //Ab 22 uhr wird nicht mehr gesaugt
        if( now.getHourOfDay() == 22) {
            ReinigungsAusfuehren = false
            return false
        }
        
        //Nur reinigen, wenn niemand da ist (keine Bewegung)
        val DateTime lastmovement_flur        = new DateTime((ts_Flur_Schlafzimmertuere_Alarm.state as DateTimeType).calendar.timeInMillis)
        val DateTime lastmovement_wohn        = new DateTime((ts_Flur_Wohnungstuere_Alarm.state as DateTimeType).calendar.timeInMillis)
        val DateTime lastmovement_balkon    = new DateTime((ts_Wohnzimmer_Balkontuere_Alarm.state as DateTimeType).calendar.timeInMillis)
        if( lastmovement_flur.plusMinutes(45).isAfter(now) ||
            lastmovement_wohn.plusMinutes(45).isAfter(now) ||
            lastmovement_balkon.plusMinutes(45).isAfter(now)) {
            
            if( cWohnzimmerLuigiStartAbwesend.state == ON) {
                logInfo(    "Wohnzimmer", String::format( "%15s | Bewegung -> verschiebe automatische Reinigung", "Luigi"))
                return false
            }
        }
        
        Thread::sleep(2000)
        ReinigungsAusfuehren = false
        
        if( Luigi_Status.state == "Offline") logInfo(    "Wohnzimmer", String::format( "%15s | Automatische Reinigung entfaellt (Offline)", "Luigi"))
        else {
            logInfo(    "Wohnzimmer", String::format( "%15s | Automatische Reinigung!", "Luigi"))
            Luigi_Do_Start.sendCommand(ON)
            
            //Fades erneut anzeigen
            Luigi_Fade_Anzahl.postUpdate(0)
        }
    }
end
2 Likes

Actually I was hoping to find that in the items file as Iā€™m struggling to get the HTTP binding to work. Must be some mean kind of doesnā€™t work-without-char-escape-or-other-magic-something and, frankly speaking, I was too lazy to go for the workaround of using rules. Thanks anyway.

Oh, btw my ā€œLuigiā€ arrived friday, WiFi hack was applied, itā€™s (ā€œheā€™sā€ ?) happily cleaning since. Iā€™m still curious how heā€™ll perform with different floor levels.

I did not use the http binding since I am dynamically changing the refresh frequency. When the Hombot is cleaning, I poll more often.
Use the code above and post an update to Luigi_UpdateStatus.
This is where you find all the information. :slight_smile:

I just got Luigi and the HTTP binding working for me:

String Hombot "Luigi, der Hombot" <luigi> (Status,Test) { http=">[1:GET:http://192.168.178.52:6260/json.cgi?%%7b%%22COMMAND%%22:%%22CLEAN_START%%22%%7d] >[0:GET:http://192.168.178.52:6260/json.cgi?%%7b%%22COMMAND%%22:%%22PAUSE%%22%%7d] >[2:GET:http://192.168.178.52:6260/json.cgi?%%7b%%22COMMAND%%22:%%22HOMING%%22%%7d] <[http://192.168.178.52:6260/status.html:5000:REGEX(.*<b>Robot-state</b>: <status>(.+)</status>.*)]" }

Thanks for the input.

2 Likes

Iā€™ve put up a Wiki doc page. Find it on openHAB wiki underneath ā€œApplication Integrationā€.

PS: and I just got my robot lawn mower automated, too. See here.

2 Likes

Anyone know anything the ILife Robot? Got it a couple of days ago and love it. @$150 on Amazon. Awesome price. Would be nice to be able to run it from openhab. how do you find a protocol that would work with it?

Hi all , I have had my IRoomba for more than 10 years now, however I think its time to upgrade it, so I was thinking about getting this one:

It has wifi, but I am not sure how easy it is to hack into thoughā€¦ Any Idea if this can be connected to OH or should I go for the more expensive LG one, that has gotten mixed review?

Running the LG for a couple of months, I can tell it is pretty fine. Granted, I donā€™t have a Roomba to compare to.
Note my OH wiki page: you donā€™t need to get the ā€˜bigā€™ 64701 Hombot. Itā€™s just about color and SW-activated features which you can have on the smaller models when you hack them.
I have a 64607, itā€™s only 40ā‚¬ more than that Xiaomi, and OH connectivity on that one is unknown, so Iā€™d avoid it.

Hi,

Iā€™m using RooWifi with my Roomba. Iā€™ve tried to read the json-output to see what state the Roomba is in, but itā€™s not finished yet.

This is using the http-binding to get JSON from the RooWifi.

The items are here:

And the rules are here:

Thereā€™s also Roomba.map and RoombaStatus.map files for translation of different states.

Hope this helps

Check this post for Roomba 980 integration on OpenHab. Now is possible.

1 Like

In OH 1.9.0+, the number of fields in the HTTP binding has changed. If the above doesnā€™t work to control your Hombot, you need to change ā€˜:ā€™ to ā€˜%%3Aā€™. Just updated the Wiki page, too.

If anyone is interested, I have written a python interface for my Roomba 980, specifically for Openhab2 use (should work for OH1 also, as it uses the mqtt binding).

Itā€™s here: Roomba 980 OH interface

See this post for screenshots etc. OH2 Roomba 980 interface

I have a relatively cheap but very good vacuum cleaner (https://www.amazon.de/16192-Saugroboter-Ladestation-Reinigungsprogrammen-Aufnahmevolumen/dp/B015XPGHVC). Got it for 80 ā‚¬ at a special sale!!

It comes with an IR remote, a docking station, invisible wall and is able to find the docking station once battery is down.

To start it via OpenHAB I simply use an arduino, an attached IR LED and MQTT. The LED is aimed at the charging station and sends the ā€œCLEANā€ command just like the remote doesā€¦

Works like a charm!! :wink:

1 Like

Try out either Roomba 860 or 880. Both of these smart floor cleaners are manufactured by a trusted company called iRobot. In this Roomba 860 vs 880 battle, Iā€™d say that the best option is to purchase Roomba 880.