Looking for an automatable vacuum cleaner

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.

I would check out Neato Connected, I have two and like them a lot.

Ever figure it out?
They’re on sale tomorrow morning on Amazon lightning deals . . so considering it.

Hey bulletprooffool, guess i didnt get back to you before the flash sale!

no, never did. i still just push the button.

I advise you to raise money and buy Roomba. From my own experience I know that this is an excellent vacuum cleaner. Here you can find Roomba cheaper. Here is a review of the models and the list, I hope you come in handy. Goodluck.

There is now. I just integrated Ecovacs Deebot with OpenHAB

1 Like