hi,
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],
>[broker:landroid/set/rainDelay:state:*:default]"}
Number Landroid_timeExtension "time Extension [%d %%]" <lawnmower> {mqtt="<[broker:landroid/status/timeExtension:state:default],
>[broker:landroid/set/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(landroid_errorcode.map):%s]" <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(landroid_statuscode.map):%s]" <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"
when
Item Landroid_errorCode changed
then
if (Landroid_errorCode.state != 0) {
sendNotification("xxxx@gmx.at", "der III. meldet " + transform("MAP", "landroid_errorcode.map", Landroid_errorCode.state.toString))
}
end
rule "for chart statistics" //to see when Landroid moves or sleeps
when
Item Landroid_StatusDescription received update
then
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)
end
rule "Landroid start/stop/sleep via mqtt"
when
Item Landi_mowing received command
then
Thread::sleep(500)
switch Landi_mowing.state {
case "1": {
if (Landroid_timeExtension.state == -100) {
var Number RtE = Rem_Landroid_timeExtension.state as DecimalType
sendCommand(Landroid_timeExtension,RtE)
}
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
sendCommand(Rem_Landroid_timeExtension,LtE)
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
sendCommand(Landroid_timeExtension,RtE)
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", "")
postUpdate(Rem_Landroid_timeExtension,LtE)
sendCommand(Landroid_timeExtension,-100)
Landi_mowing.label = "der III., hat Auszeit!!!"
logInfo("Landroid.rules","Auszeit einschalten, III go home")
}
logInfo("Landroid.rules","landi_mowing: " + Landi_mowing.state)
}
}
end
rule "label update"
when
Item Landroid_StatusDescription received update or
Item Landroid_batteryLevel received update
then
Thread::sleep(250)
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!!!"
end
rule "runtime calc"
when
Time cron "0 0 0 1/1 * ? *" //00:00
then
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)
end
rule "diff for runtime and distance"
when
Time cron "0 55 23 1/1 * ? *" //23:55
then
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)
end
rule "remember that Landroid sleeps"
when
Time cron "0 0 8 1/1 * ? *" //09:00
then
logInfo("remember Landi sleeps","Landi_mowing: " + Landi_mowing.state)
if (Landi_mowing.state == 2) {
sendNotification("xxxxx@gmx.at", "der III. ist immer noch im Schlafmodus!")
}
end
rule "mowing time depening on season name"
when
Item Season changed
then
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)
end
rule "Landroid: it is raining"
when
Item Raining changed
then
Thread::sleep(250)
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", "")
postUpdate(Rem_Landroid_timeExtension,LtE)
sendCommand(Landroid_timeExtension,-100)
Landi_mowing.label = "der III., ist kein Fisch!!!"
logInfo("Landroid.rules","Es regnet, III go home")}
case "OFF": {if (Landi_mowing.state !=2)
postUpdate(Landroid_timeExtension,rtE)
Landi_mowing.label = "der III.,: " + Landroid_StatusDescription.state.toString
logInfo("Landroid.rules","Es regnet nicht mehr, III mach weiter")}
}
end
rule "switch rain on/off"
when
Item Condition changed
then
Thread::sleep(250)
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)
//rainTimer.cancel
else (sendCommand(Raining,OFF))
logInfo("Landroid.rules","Es regnet seit 2h nicht mehr")])
}
end
maybe my post gives someone else some ideas how to operate a roboter mower through openhab.
if
someone finds any improvements in rules etc
Then please don´t hesitate to tell me
else if (any comments welcome!)
end