Hi together,
I am watching and observing the washing machine state machine thread (What a tongue twister ) for a while now.
I definetely want to connect my washing machine in the same way later,
but currently i dont want to buy an extra switch that is able to measure.
Anyway i wanted to get noticed from my machine, since its located on another floor in this house.
So i was figuring out a low cost way, until i will switch my local setup to a state machine too.
Maybe this is a good “temporary” solution for someone else or in another usecase,
so i decided to share it with you.
Simple solution:
- An Amazon Dash Button which costs 4.99€ (that will get refunded at the first button-order)
- A little Rule
- A Telegram/Pushover Action
I ordered a Dash Button, bought some washing powder and got this button theoretically “for free”.
The Rule is based on the preferred washing programms in our home.
Basically I could break the logic down to three different times, we have measured.
- The standard programm with about 1h and 43min duration
- The extended programm with about 2h and 23min duration
- The spin dry programm with 12min duration
I implemented this behavior with four Dash Button press-triggers.
Three for the different programs and one to reset all timers.
washingmachine.items:
// Washing Machine-Items
Switch rWashingMachineTimer_active "Waschmaschinen-Timer Aktiv" <clock> (wmTimerStatus)
DateTime rWashingMachineTimer_TimerEnd "Waschmaschinen-Timer [%1$tH:%1$tM Uhr]" <washingmachine> (wmTimerStatus)
Number rWashingMachineTimer_TimerDuration "Waschmaschinen-Timer Restzeit [%d min]" <washingmachine> (wmTimerStatus)
Number rWashingMachineTimer_choosenProgramm "Gewähltes Programm: [MAP(washingMachine.map):%s]" <washingmachine> (wmTimerStatus)
washingmachine.rules:
// Imports
import org.joda.time.*
// Variables
var Timer tWashingMachine // Timer for the alarm itself
var Timer tProgramFeedback // Timer for feedback of the choosen program
var DateTime startTimeStorage // Time stored at the first button-press-trigger
var int buttonPressCounter // Counter for the amount of button-press-trigger-events
// Read-Only values
val String filename = "washingMachine.rules"
// Timer presets
val int standardProgramDuration = 110 // 1 hour and 43 minutes
val int extendedProgramDuration = 150 // 2 hour and 23 minutes
val int spinDryProgramDuration = 15 // 12 minutes
val int feedbackAfterDuration = 5 // 5 minutes
rule "Washing Machine Timer Init"
when
System started
then
if (rWashingMachineTimer_active.state == NULL) rWashingMachineTimer_active.postUpdate(OFF)
end
rule "Washing Machine Duration Update"
when
Time cron "0 * * * * ?" // Cron every minute
then
// Check if a timer is running
if (rWashingMachineTimer_active.state == ON) {
logInfo(filename, "updating timer duration")
var DateTime timeEnd = null;
logInfo(filename, "rWashingMachineTimer_TimerEnd.state " + rWashingMachineTimer_TimerEnd.state.toString)
if(rWashingMachineTimer_TimerEnd.state == NULL){
logInfo(filename, "updating first time")
// Create timeEnd in case of a timer end NULL value
timeEnd = new DateTime()
logInfo(filename, "updating first time completed")
}
else{
logInfo(filename, "updating again")
// Update timeEnd to the timer end value
timeEnd = new DateTime(DateTime.parse(rWashingMachineTimer_TimerEnd.state.toString()))
logInfo(filename, "updating again completed")
}
logInfo(filename, "Deadline: " + timeEnd)
// Calculate difference between now and timer end to get the duration
var Number timeDuration = ((((timeEnd.millis - now.millis) / 1000) + 30) / 60).intValue()
// Keep duration on a minimum of 0, since timer end can be in the past
if(timeDuration <= 0){
timeDuration = 0
}
logInfo(filename, "Duration: " + timeDuration)
// Update duration item
rWashingMachineTimer_TimerDuration.postUpdate(timeDuration)
}
else{
// Keep duration on 0 minutes while timer is inactive
rWashingMachineTimer_TimerDuration.postUpdate(0)
}
end
rule "Washing Machine Dash button pressed"
when
Channel "amazondashbutton:dashbutton:bio:press" triggered
then
// No Timer active yet
if(tWashingMachine == null){
// Store the first button-press-action-time and initalize press-counter
startTimeStorage = now()
//Initalize first press trigger in the counter variable
buttonPressCounter = 1
// Update programm item to "standard programm"
rWashingMachineTimer_choosenProgramm.postUpdate(1)
// Set timer active item to ON
rWashingMachineTimer_active.postUpdate(ON)
logInfo("washingMachineDashButtonEvent", "Waschmaschinentimer Startzeitpunkt: " + startTimeStorage.toString("H:m:s"))
// Update timer end to preset 1
var preset = startTimeStorage.plusMinutes(standardProgramDuration)
rWashingMachineTimer_TimerEnd.postUpdate(new DateTimeType (preset.toCalendar(null)))
// Start timer with first preset
tWashingMachine =
createTimer(startTimeStorage.plusMinutes(standardProgramDuration))[
logInfo("washingMachineDashButtonEvent","\"Standardprogramm fertig\" Meldung ausgelöst.")
pushover("Waschmaschine Standardprogramm fertig")
// Reset main timer
tWashingMachine.cancel()
tWashingMachine = null
// Update programm item to "no timer choosen"
rWashingMachineTimer_choosenProgramm.postUpdate(0)
// Set timer active item to OFF
rWashingMachineTimer_active.postUpdate(OFF)
]
logInfo("washingMachineDashButtonEvent", "Standard Timer (" + standardProgramDuration.toString + " min) gestartet.")
}
// A timer is already started
else if (tWashingMachine != null){
// Increase button counter
buttonPressCounter++
logInfo("washingMachineDashButtonEvent", "Button wurde - " + buttonPressCounter + " - mal gedrückt - Startzeitpunkt war: " + startTimeStorage.toString("H:m:s") + ".")
// Start timer with second preset
if(buttonPressCounter == 2){
logInfo("washingMachineDashButtonEvent", "Ändere Timer auf Langes Waschprogramm.")
// Cancel currently running timer
tWashingMachine.cancel()
// Update programm item to "extended programm"
rWashingMachineTimer_choosenProgramm.postUpdate(2)
// Update timer end to preset 2
var preset2 = startTimeStorage.plusMinutes(extendedProgramDuration)
rWashingMachineTimer_TimerEnd.postUpdate(new DateTimeType (preset2.toCalendar(null)))
// Set up new timer for the now choosen programm
tWashingMachine =
createTimer(startTimeStorage.plusMinutes(extendedProgramDuration))[
logInfo("washingMachineDashButtonEvent", "\"Langes Waschprogramm fertig\" Pushmeldung ausgelöst.")
pushover("Langes Waschprogramm fertig")
// Reset main timer
tWashingMachine.cancel()
tWashingMachine = null
// Update programm item to "no timer choosen"
rWashingMachineTimer_choosenProgramm.postUpdate(0)
// Set timer active item to OFF
rWashingMachineTimer_active.postUpdate(OFF)
]
logInfo("washingMachineDashButtonEvent", "Langes Waschprogramm Timer (" + extendedProgramDuration.toString + " min) gestartet.")
}
// Start timer with third preset
if(buttonPressCounter == 3){
logInfo("washingMachineDashButtonEvent", "Ändere Timer auf Schleuderprogramm.")
// Cancel currently running timer
tWashingMachine.cancel()
// Update programm item to "spin drying programm"
rWashingMachineTimer_choosenProgramm.postUpdate(3)
// Update timer end to preset 3
var preset3 = startTimeStorage.plusMinutes(spinDryProgramDuration)
rWashingMachineTimer_TimerEnd.postUpdate(new DateTimeType (preset3.toCalendar(null)))
// Set up new timer for the now choosen programm
tWashingMachine =
createTimer(startTimeStorage.plusMinutes(spinDryProgramDuration))[
logInfo("washingMachineDashButtonEvent", "\"Schleuderprogramm fertig\" Pushmeldung ausgelöst.")
pushover("Schleuderprogramm fertig")
// Reset main timer
tWashingMachine.cancel()
tWashingMachine = null
// Update programm item to "no timer choosen"
rWashingMachineTimer_choosenProgramm.postUpdate(0)
// Set timer active item to OFF
rWashingMachineTimer_active.postUpdate(OFF)
]
logInfo("washingMachineDashButtonEvent", "Schleuderprogramm Timer (" + spinDryProgramDuration.toString + " min) gestartet.")
}
// reset timer
if(buttonPressCounter == 4){
logInfo("washingMachineDashButtonEvent", "Setze Waschmaschinen-Timer zurück...")
// Reset main timer
tWashingMachine.cancel()
tWashingMachine = null
// Update programm item to "everything cancelled"
rWashingMachineTimer_choosenProgramm.postUpdate(4)
// Set timer active item to OFF
rWashingMachineTimer_active.postUpdate(OFF)
// Set timer end to now, for cronjob
rWashingMachineTimer_TimerEnd.postUpdate(new DateTimeType (now()))
logInfo("washingMachineDashButtonEvent", "Alle Waschmaschinen Timer zurückgesetzt.")
}
}
end
rule "Washing Machine Programm Feedback"
when
Item rWashingMachineTimer_choosenProgramm changed
then
// Reset Feedback-Timer in case of programm change
if(tProgramFeedback != null)
tProgramFeedback.cancel()
if(rWashingMachineTimer_choosenProgramm.state != NULL && rWashingMachineTimer_choosenProgramm.state != 0){
logInfo(filename, "Feedback pushover timer started")
// Start Feedback Timer for choosen Program
// the finally choosen Timer will be pushed in "feedbackAfterDuration" minutes after the first button-press-event
tProgramFeedback=
createTimer(now.plusMinutes(feedbackAfterDuration))[
logInfo(filename, "Feedback Pushover Switch fired")
switch rWashingMachineTimer_choosenProgramm.state {
case 1: pushover("[Waschmaschinentimer] Standardprogramm gestartet")
case 2: pushover("[Waschmaschinentimer] Langes Programm gestartet")
case 3: pushover("[Waschmaschinentimer] Schleuderprogramm gestartet")
case 4: pushover("[Waschmaschinentimer] Alle Timer manuell resettet")
}
]
}
end
To keep the times as exactly as possible, the rule stores the exact time of the first button trigger event.
The second and third trigger event are using that datetime as startpoint.
The fourth trigger resets all active timers and the timers reset itself of course after firing the notification event.
Pro:
- Low Cost
- Low effort needed
Contra:
- Only a workaround compared to a holistic approach like the state machine
- You have to wait for the dash button to complete its trigger, until you can press again
Conclusion (for me):
Of course this is a very simple approach (no rocket science included this time ),
but maybe it can help someone and it works fine as my current workaround here.
(I am reading and learning from the community for a while now, so i thought it would be nice to give at least something small back to the com.)
I am open to feedback or improvement suggestions for sure.