Howdy! Here’s a script I’ve written to monitor my washing machines and send a notification when the laundry is done. It’s not a unique premise for sure, but it may possibly be easier to set up than threshold-based state machines, since it relies on delayed reaction rather than exact power usage. Posting it in the hope that it’s some use to somebody. Would also love suggestions of simplifications, particularly if there’s a way to generalize the rule creation and keep it inside the class.
# This script monitors one or more washing machines (power usage), based on TIME as well as power threshold,
# and sends a Broadcast Notification when the cycle is done.
# Version 2
from core.rules import rule
from core.triggers import when
from threading import Timer
from org.joda.time import DateTime
from org.openhab.io.openhabcloud import NotificationAction
from core.log import logging, LOG_PREFIX
log = logging.getLogger(LOG_PREFIX + ".WashingMachines")
def logprint(text):
log.debug(text)
class WashingMachineMonitor(object):
def __init__(self, strItemname, strMessage):
self.strItemname=strItemname
self.strMessage=strMessage
self.hTimerActivate=None
self.hTimerDeactivate=None
self.bActive=False
self.bUsingPower=False
self.fThreshold=5 #watts
self.fActivateTimeout=30 #seconds to realize we're doing laundry
self.fDeactivateTimeout=180 #seconds to realize laundry is done
def fnTimerActivate(self):
logprint("post-activate " + self.strItemname)
self.hTimerActivate=None
# We've been on for a while, we must be doing laundry
self.bActive=True
logprint(self.strItemname + " entering washing cycle")
def fnTimerDeactivate(self):
logprint("post-deactivate " + self.strItemname)
self.hTimerDeactivate=None
# We've been off for a while. Were we on long enough to be doing laundry?
if self.bActive:
# Yes. Laundry cycle is done!
self.bActive=False
logprint(self.strItemname + " laundry cycle done!")
# Send the notification through the openHAB cloud binding
NotificationAction.sendBroadcastNotification(self.strMessage)
def fnActivity(self,fPowerWatts):
#logprint(self.strItemname + " currently using " + str(fPowerWatts))
bUsingPowerNow=True if fPowerWatts > self.fThreshold else False
if self.bUsingPower != bUsingPowerNow:
self.bUsingPower = bUsingPowerNow
if self.hTimerActivate:
self.hTimerActivate.cancel()
self.hTimerActivate=None
if self.hTimerDeactivate:
self.hTimerDeactivate.cancel()
self.hTimerDeactivate=None
if self.bUsingPower:
#logprint(self.strItemname + " start activate timer, using " + str(fPowerWatts))
self.hTimerActivate=Timer(self.fActivateTimeout, self.fnTimerActivate)
self.hTimerActivate.start()
else:
#logprint(self.strItemname + " start deactivate timer, using " + str(fPowerWatts))
self.hTimerDeactivate=Timer(self.fDeactivateTimeout, self.fnTimerDeactivate)
self.hTimerDeactivate.start()
# Define how many appliances to monitor here.
# The first parameter is what item to monitor.
# However, it is not used yet because I wasn't sure how to define the rules in a generic manner.
# I included the parameter for a future update.
# The second parameter is the message to send, in this case using a unicode string.
TopLoader = WashingMachineMonitor("WashingMachineTopLoaderWatts",u"เครื่องซักผ้าเสร็จแล้วครับ (ฝาบน)")
FrontLoader = WashingMachineMonitor("WashingMachineFrontLoaderWatts",u"เครื่องซักผ้าเสร็จแล้วครับ (ฝาหน้า)")
# Since I'm not yet sure how to define a rule in a generic manner, we define one for each washing machine.
# It should be triggered by the power usage of each appliance, and sending the power usage in watts to the
# respective object through the fnActivity function
@rule("WashingMachineTopLoaderWatts", description="", tags=["utility"])
@when("Item WashingMachineTopLoaderWatts received update")
def WashingMachineTopLoaderWatts_Decorators(event):
TopLoader.fnActivity(event.itemState.floatValue())
@rule("WashingMachineFrontLoaderWatts", description="", tags=["utility"])
@when("Item WashingMachineFrontLoaderWatts received update")
def WashingMachineFrontLoaderWatts_Decorators(event):
FrontLoader.fnActivity(event.itemState.floatValue())
Original version of my script for posterity
# This script monitors one or more washing machines (power usage), based on TIME as well as power threshold,
# and sends a Broadcast Notification when the cycle is done.
# Version 1
from org.slf4j import LoggerFactory
from core.rules import rule
from core.triggers import when
from threading import Timer
from org.joda.time import DateTime
from org.openhab.io.openhabcloud import NotificationAction
def logprint(text):
pass
LoggerFactory.getLogger("org.eclipse.smarthome.automation.examples").info(text)
class WashingMachineMonitor(object):
def __init__(self, strItemname, strMessage):
self.strItemname=strItemname
self.strMessage=strMessage
self.hTimerActivate=None
self.hTimerDeactivate=None
self.bActive=False
self.bUsingPower=False
self.fThreshold=5 #watts
self.fActivateTimeout=30 #seconds to realize we're doing laundry
self.fDeactivateTimeout=180 #seconds to realize laundry is done
def fnTimerActivate(self):
logprint("post-activate " + self.strItemname)
self.hTimerActivate=None
# We've been on for a while, we must be doing laundry
self.bActive=True
logprint(self.strItemname + " entering washing cycle")
def fnTimerDeactivate(self):
logprint("post-deactivate " + self.strItemname)
self.hTimerDeactivate=None
# We've been off for a while. Were we on long enough to be doing laundry?
if self.bActive:
# Yes. Laundry cycle is done!
self.bActive=False
logprint(self.strItemname + " laundry cycle done!")
"""
Currently, JSR223/Jython in OpenHAB cannot handle unicode strings, but Rules DSL can, so we don't send the notification directly.
Instead, we hand the job off to a rule, so that we can change the message along the way.
First we define an item (in a .items file)
String NotifyLeif "Send string to Leif's mobile"
Then we define a rule (in a .rules file):
rule "NotifyLeif rule"
when
Item NotifyLeif received update
then
var strMessage=NotifyLeif.state.toString
logInfo("rule","NotifyLeif: " + strMessage);
switch(NotifyLeif.state.toString)
{
case "TOPLOADER":
strMessage="เครื่องซักผ้าเสร็จแล้วครับ (ฝาบน)"
case "FRONTLOADER":
strMessage="เครื่องซักผ้าเสร็จแล้วครับ (ฝาหน้า)"
}
sendBroadcastNotification(strMessage)
end
"""
#NotificationAction.sendBroadcastNotification(self.strMessage)
events.postUpdate(ir.getItem("NotifyLeif"),self.strMessage)
def fnActivity(self,fPowerWatts):
#logprint(self.strItemname + " currently using " + str(fPowerWatts))
bUsingPowerNow=True if fPowerWatts > self.fThreshold else False
if self.bUsingPower != bUsingPowerNow:
self.bUsingPower = bUsingPowerNow
if self.hTimerActivate:
self.hTimerActivate.cancel()
self.hTimerActivate=None
if self.hTimerDeactivate:
self.hTimerDeactivate.cancel()
self.hTimerDeactivate=None
if self.bUsingPower:
#logprint(self.strItemname + " start activate timer, using " + str(fPowerWatts))
self.hTimerActivate=Timer(self.fActivateTimeout, self.fnTimerActivate)
self.hTimerActivate.start()
else:
#logprint(self.strItemname + " start deactivate timer, using " + str(fPowerWatts))
self.hTimerDeactivate=Timer(self.fDeactivateTimeout, self.fnTimerDeactivate)
self.hTimerDeactivate.start()
# Define how many appliances to monitor here.
# The first parameter is what item to monitor. However, it is not used yet because I wasn't sure how to define the rules in a generic manner.
# I included the parameter for a future update.
TopLoader = WashingMachineMonitor("WashingMachineTopLoaderWatts","TOPLOADER")
FrontLoader = WashingMachineMonitor("WashingMachineFrontLoaderWatts","FRONTLOADER")
# Since I'm not yet sure how to define a rule in a generic manner, we define one for each washing machine.
# It should be triggered by the power usage of each appliance, and sending the power usage in watts to the
# respective object through the fnActivity function
@rule("WashingMachineTopLoaderWatts", description="", tags=["utility"])
@when("Item WashingMachineTopLoaderWatts received update")
def WashingMachineTopLoaderWatts_Decorators(event):
TopLoader.fnActivity(event.itemState.floatValue())
@rule("WashingMachineFrontLoaderWatts", description="", tags=["utility"])
@when("Item WashingMachineFrontLoaderWatts received update")
def WashingMachineFrontLoaderWatts_Decorators(event):
FrontLoader.fnActivity(event.itemState.floatValue())