My Landroid-S Roboter Mower

(Matthias P) #1

I´m playing with openHAB since almost a year now. It became a real hobby for me, spending lots of hours learning linux basics, programming and soldering things together to customize my house automation. with the help from tons of tutorials and posts from this community together with my own trial and errors I got from an experimental playground to a fully operational home automation solution. my latest implementation is a roboter mower, which was on my christmas wishlist for many years. thanks to openhab it got prio 1.
the mower, we simply call “the Third”, is still in the socialising phase, meaning that family members need to learn to not let any tools or toys laying around on the lawn. our cats went from fear to curious to ignoring within a couple of days… this is the solution how I implemented our mower into openhab:

The mower:

I read lots of blogs and test comparison reports about various brands and at the end my choice fell on the Worx Landroid-S mower. It perfectly fits the size of my garden, it is small enough to reach thight spots behind some trees and it has a zonecontrol which allows me to navigate the mower to a small area behind the pool which is difficult to target by randomized movements. but the most important feature is the possibility to connect the mower to openhab.

The API:

someone developed a “bridge” for the Landroid-S mowers that allows status, statistics and commands to be sent and retrieved from the device:

I installed the bridge on the same raspberry Pi that also hosts openhab. the bridge can also integrate a weather provider but this didn`t work well for me, so I only used the basic functions. I use MQTT to communicate between openhab and bridge as mosquitto was already in place for other devices…

The functions:

the mower comes with an mobile app that gives you total control of the mower, so what is the added value of openhab?
the app has no notification service in the case that the mower needs attention. with openhab the mower can alert me when it´s stuck has lost or cut the guidance wire or fell down the cellar bay.
the app doesn´t tell the mower when it´s raining. so it will happen that the mower starts it´s job and comes back after 5 minutes because it´s rainsensor got wet. thanks to openhab it stays home and waits till the grass is dry before it starts to work again.
when kids are playing in the garden I can tell the mower to take off for the rest of the day.
another nice feature is that the mower now automatically adjusts the mowing time depending on the season of the year. as known grass grows faster in spring than in autum.
last but not least openhab can show various statistics like daily mowing time, charging times and required power.

The GUI:

The items:

Group gLandroid "Landi statistics"

String Landroid_DateTime "Last Update [%s]" <calendar> {mqtt="<[broker:landroid/status/dateTime:state:default]"}
Number Landroid_firmware "Firmware [v%s]" <lawnmower> {mqtt="<[broker:landroid/status/firmware:state:default]"}
Number Landroid_wifiQuality "Wifi Quality [SCALE(signalstrength.scale):%s]" <lawnmower> {mqtt="<[broker:landroid/status/wifiQuality:state:default]"}
String Landroid_active "Active [%s]"  <lawnmower> {mqtt="<[broker:landroid/status/active:state:default]"}
Number Landroid_rainDelay "rain Delay [%d min]"  <lawnmower> {mqtt="<[broker:landroid/status/rainDelay:state:default],
Number Landroid_timeExtension "time Extension [%d %%]"  <lawnmower> {mqtt="<[broker:landroid/status/timeExtension:state:default],
String Landroid_serialNumber "Serial Number [%d]"  <lawnmower> {mqtt="<[broker:landroid/status/serialNumber:state:default]"}
Number Landroid_totalTime "Total Time [JS(minutestohours.js):%s]"  <lawnmower> {mqtt="<[broker: landroid/status/totalTime:state:default]"}
Number:Length Landroid_totalDistance "Total Distance [%s m]"  <lawnmower> {mqtt="<[broker:landroid/status/totalDistance:state:default]"}     
Number Landroid_totalBladeTime "Total Bladetime [JS(minutestohours.js):%s]"  <lawnmower> {mqtt="<[broker:landroid/status/totalBladeTime:state:default]"}
Number Landroid_batteryChargeCycle "Battery ChargeCycle [%d]"  <lawnmower> {mqtt="<[broker:landroid/status/batteryChargeCycle:state:default]"}
String Landroid_batteryCharging "Battery charging [%s]" <lawnmower> {mqtt="<[broker:landroid/status/batteryCharging:state:default]"}
Number Landroid_batteryVoltage "Battery Voltage [%.2f V]"  <lawnmower> (gLandroid) {mqtt="<[broker:landroid/status/batteryVoltage:state:default]"}
Number Landroid_batteryTemperature "Battery Temperature [%.1f °C]" <lawnmower> (gLandroid) {mqtt="<[broker:landroid/status/batteryTemperature:state:default]"}
Number Landroid_batteryLevel "Battery Level [%d %%]"  <lawnmower> (gLandroid) {mqtt="<[broker:landroid/status/batteryLevel:state:default]"}
Number Landroid_errorCode "Error Code [MAP(]"  <lawnmower> {mqtt="<[broker:landroid/status/errorCode:state:default]"}
String Landroid_ErrorDescription "Error [%s]" <lawnmower> {mqtt="<[broker:landroid/status/errorDescription:state:default]"}
Number Landroid_statusCode "Status Code [MAP(]"  <lawnmower> {mqtt="<[broker:landroid/status/statusCode:state:default]"}
String Landroid_StatusDescription "Status [%s]" <sheep> {mqtt="<[broker:landroid/status/statusDescription:state:default]"}

//following items are for statistics and actions
Number Landi_mows (gLandroid) 
Number Landroidstatistic
String Landi_mowing 
Number Landroid_totalTime_yesterday  "totaltimeyesterday [%d]" <lawnmower>
Number Landroid_runTime_yesterday "Runtime yesterday [JS(minutestohours.js):%s]" <lawnmower>
Number:Length Landroid_totalDistance_yesterday "totaldistyesterday [%d]" <lawnmower>
Number:Length Landroid_Distance_yesterday "Distance yesterday [%.2f m]" <lawnmower>
Number Rem_Landroid_timeExtension 

The Rules:

       rule "Landroid out of control"
        Item Landroid_errorCode changed
                if (Landroid_errorCode.state != 0) {
                    sendNotification("", "der III. meldet " + transform("MAP", "", Landroid_errorCode.state.toString))

rule "for chart statistics" //to see when Landroid moves or sleeps
        Item Landroid_StatusDescription received update 
                if (Landroid_StatusDescription.state.toString.contains("Mowing") || Condition.state.toString.contains("Cutting")) sendCommand(Landi_mows,100) 
                else if (Landroid_StatusDescription.state.toString.contains("Home")) sendCommand(Landi_mows,0)        

rule "Landroid start/stop/sleep via mqtt"
        Item Landi_mowing received command
                switch Landi_mowing.state {
                case "1": {
                        if (Landroid_timeExtension.state == -100) {
                            var Number RtE = Rem_Landroid_timeExtension.state as DecimalType
                        else if (Landroid_timeExtension.state > -100) {
                            logInfo("Landroid.rules","starten, mqttstart")
                            publish("broker", "landroid/set/start", "")
                        logInfo("Landroid.rules","landi_mowing: " + Landi_mowing.state)
                case "0": {
                        var Number LtE = Landroid_timeExtension.state as DecimalType
                        publish("broker", "landroid/set/stop", "")
                        logInfo("Landroid.rules","landi_mowing: " + Landi_mowing.state)
                case "2": {
                    if (Landroid_timeExtension.state == -100) {
                        var Number RtE = Rem_Landroid_timeExtension.state as DecimalType
                        Landi_mowing.label = "der III., Status: " + Landroid_StatusDescription.state.toString
                        logInfo("Landroid.rules","Auszeit ausschalten, battery charging")
                    else if (Landroid_timeExtension.state > -100) {
                        var Number LtE = Landroid_timeExtension.state as DecimalType
                        publish("broker", "landroid/set/stop", "")
                        Landi_mowing.label = "der III., hat Auszeit!!!" 
                        logInfo("Landroid.rules","Auszeit einschalten, III go home")
                    logInfo("Landroid.rules","landi_mowing: " + Landi_mowing.state)

rule "label update"
        Item Landroid_StatusDescription received update or
        Item Landroid_batteryLevel received update
                if (Landroid_batteryCharging.state.toString.contains("true") && Landi_mowing.state < 2) Landi_mowing.label = "der III., Charging: " + Landroid_batteryLevel.state.toString + "%"
                else if (Landroid_batteryCharging.state.toString.contains("false") && Landi_mowing.state < 2 && Raining.state == OFF) Landi_mowing.label = "der III.,: " + Landroid_StatusDescription.state.toString 
                else if (Landroid_batteryCharging.state.toString.contains("false") && Landi_mowing.state == 2 ) Landi_mowing.label = "der III., hat Auszeit!!!"  

rule "runtime calc"
        Time cron "0 0 0 1/1 * ? *"  //00:00
                val Number Lyt = Landroid_totalTime.state as Number
                postUpdate(Landroid_totalTime_yesterday, Lyt)
                val Number Lyd = Landroid_totalDistance.state as Number
                postUpdate(Landroid_totalDistance_yesterday, Lyd)

rule "diff for runtime and distance"
        Time cron "0 55 23 1/1 * ? *"  //23:55
                val Number Ltt = Landroid_totalTime.state as Number
                val Number Ltty = Landroid_totalTime_yesterday.state as Number
                val Number rty = (Ltt - Ltty)
                postUpdate(Landroid_runTime_yesterday, rty)
                val Number Ltd = Landroid_totalDistance.state as Number
                val Number Ltdy = Landroid_totalDistance_yesterday.state as Number
                val Number dy = (Ltd - Ltdy)
                postUpdate(Landroid_Distance_yesterday, dy)    

rule "remember that Landroid sleeps"
        Time cron "0 0 8 1/1 * ? *"  //09:00 
                logInfo("remember Landi sleeps","Landi_mowing: " + Landi_mowing.state)
                if (Landi_mowing.state == 2) {
                    sendNotification("", "der III. ist immer noch im Schlafmodus!") 

rule "mowing time depening on season name"
        Item Season changed 
                if (Season_Name.state.toString.contains("SPRING")) (Landroid_timeExtension.state == 50)
                else if (Season_Name.state.toString.contains("SUMMER")) (Landroid_timeExtension.state == 0)
                else if (Season_Name.state.toString.contains("AUTUMN")) (Landroid_timeExtension.state == -30)

rule "Landroid: it is raining"
        Item Raining changed
                var Number LtE = Landroid_timeExtension.state as DecimalType
                var Number rtE = Rem_Landroid_timeExtension.state as DecimalType
                switch (Raining.state.toString) {
                    case "ON": {if (Landi_mowing.state !=2) 
                        publish("broker", "landroid/set/stop", "")
                        Landi_mowing.label = "der III., ist kein Fisch!!!" 
                        logInfo("Landroid.rules","Es regnet, III go home")}
                    case "OFF": {if (Landi_mowing.state !=2) 
                        Landi_mowing.label = "der III.,: " + Landroid_StatusDescription.state.toString
                        logInfo("Landroid.rules","Es regnet nicht mehr, III mach weiter")}

rule "switch rain on/off"
        Item Condition changed
                if (Condition.state.toString.contains("rain")) sendCommand(Raining,ON)
                else if (Raining.state == ON) {
                    var Timer rainTimer = null
                    rainTimer = createTimer(now.plusMinutes((Landroid_rainDelay.state as DecimalType).intValue), [|
                        if (Condition.state.toString.contains("rain")) sendCommand(Raining,ON)
                        else (sendCommand(Raining,OFF))
                    logInfo("Landroid.rules","Es regnet seit 2h nicht mehr")])

maybe my post gives someone else some ideas how to operate a roboter mower through openhab.

    someone finds any improvements in rules etc
       Then please don´t hesitate to tell me
       else if (any comments welcome!)

(scott dee) #2

this all looks great, i’m edging closer and closer to getting a landroid myself so this will be an excellent addition.

(Stefan Kühnen) #3

Hi Matthias,

thanks for your input. I currently setting up my Landroid with openHAB. Would you mind to publish your Landi sitemap?

Thanks and regards

(Matthias P) #4

hi Stefan,
here we go…

Switch item=Landi_mowing icon="sheep" mappings=[0="Stoppen", 2="Auszeit"] visibility=[Landroid_StatusDescription == "Mowing"]
Switch item=Landi_mowing icon="sheep" mappings=[1="Starten", 2="Auszeit"] visibility=[Landroid_StatusDescription == "Home"]
Text item=Landroid_ErrorDescription icon="sheep" visibility=[Landroid_errorCode != 0 ]
Text item=Landroid_StatusDescription label="Statistics von der III." {
                    Text item=Landroid_DateTime 
                    Text item=Landroid_active
                    Text item=Landroid_firmware
                    Text item=Landroid_wifiQuality
                    Setpoint item=Landroid_rainDelay minValue=30 maxValue=180 step=10
                    Text item=Landroid_serialNumber
                    Text item=Landroid_totalTime
                    Text item=Landroid_runTime_yesterday
                    Text item=Landroid_totalDistance label="Total Distance [%.2f km]"
                    Text item=Landroid_Distance_yesterday label="Distance yesterday [%.2f km]" 
                    Text item=Landroid_totalBladeTime
                    Text item=Landroid_batteryChargeCycle
                    Text item=Landroid_batteryCharging
                    Text item=Landroid_batteryVoltage
                    Text item=Landroid_batteryTemperature
                    Text item=Landroid_batteryLevel
                    Text item=Landroid_errorCode
                    Text item=Landroid_ErrorDescription
                    Text item=Landroid_statusCode
                    Setpoint item=Landroid_timeExtension minValue=-100 maxValue=100 step=10
                    Switch item=PowerPlug1_Switch
                    Text item=PowerPlug1_Amp
                    Text item=PowerPlug1_kWh
                    Text item=PowerPlug1_watts
                    Text item=PowerPlug1_volts
                    Switch item=PowerPlug1_alarm
                    Switch item=Landroidstatistic label="Statistics" icon="line" mappings=[0="4h", 1="12h", 2="24h", 3="3 Tage"]
                    Chart item=gLandroid period=4h refresh=60000 legend=true visibility=[Landroidstatistic==0]
                    Chart item=gLandroid period=12h refresh=60000 legend=true visibility=[Landroidstatistic==1, Landroidstatistic=="NULL"]
                    Chart item=gLandroid period=D refresh=60000 legend=true visibility=[Landroidstatistic==2]
                    Chart item=gLandroid period=3D refresh=60000 legend=true visibility=[Landroidstatistic==3]

(BeNe) #5

Hi Matthias,

thanks for your great write up and examples. I´m at the same point with OpenHAB and MQTT :grinning:
Can you please publish your transformation files (

Thank you!

(Matthias P) #6
1=Mäher festgefahren
2=Mäher angehoben
3=Draht fehlt
4=ausserh. der Begrenzung
5=wegen Regen gestoppt
8=Fehler Messermotor
9=Fehler Fahrmotor
11=Mäher umgekippt
12=Akku leer
13=Draht vertauscht
14=Akku Ladefehler
15=Station n. gefunden
16=Mäher gesperrt
17=Akkutemp. zu hoch