Washing Machine State Machine

Hello all,

I bought some Gosund SP1 and flashed them with ESPurna. All values are arriving via MQTT and can be displayed in BasicUI.
Then I wanted to try the magic thing from first post. The weired thing is, I see no status message in UI but no error message in logs. Maybe I may ask you for a little help to check my settings?

items:

Switch  Dose_WaMa_Switch      "Steckdose Waschmaschine"  <switchwash>   (gKE_Naehen) 	{ mqtt=">[mymosquitto:Dose-WaMa/relay/0/set:command:*:MAP(ohtoespurna.map)], <[mymosquitto:Dose-WaMa/relay/0:state:MAP(espurnatooh.map)]" }

Number	Dose_WaMa_Power	    "WaMa Leistung [%.0f W]"		<ownvoltage>	(gKE_Naehen)	{ mqtt="<[mymosquitto:Dose-WaMa/power:state:default]" }
Number	Dose_WaMa_Voltage	"WaMa Spannung [%.0f V]"		<ownvoltage>	(gKE_Naehen)	{ mqtt="<[mymosquitto:Dose-WaMa/voltage:state:default]" }
Number	Dose_WaMa_Energy	"WaMa Verbrauch [%.3f kWh]"		<ownvoltage>	(gKE_Naehen)	{ mqtt="<[mymosquitto:Dose-WaMa/energy:state:default]" }
Number	Dose_WaMa_Current	"WaMa Stromstärke [%.3f A]"		<ownvoltage>	(gKE_Naehen)	{ mqtt="<[mymosquitto:Dose-WaMa/current:state:default]" }
String  WaMa_Status         "Waschmaschine Status [MAP(waschmaschine.map):%s]"    <switchwash>   (gKE_Naehen)

map:

2=Laeuft
3=Fertig
-=-
NULL=-
/*ANFANG SubFrame Keller ########################################################################################*/
	Text label=Keller icon="cellar"{
                
                Frame {
                        Group item=gKE_Werkstatt  label="Werkstatt"	icon="owntoolshop"
                        /*Text item=Werkstatt  icon="owntoolshop"{
                                Default		item=TemperatureAussenHinten		label="TemperaturHinten: [%.1f °C]"
                        }*/
                        Group item=gKE_Naehen  label="Nähzimmer"	icon="ownkate"
                        }
                }
        }

/*ENDE SubFrame Keller ########################################################################################*/

and finally rules:

import java.util.concurrent.locks.ReentrantLock

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3
var java.util.concurrent.locks.ReentrantLock finishLock  = new java.util.concurrent.locks.ReentrantLock()

rule "Waschmaschine aktiv"
when
    Item Dose_WaMa_Power changed
then
    logInfo ("WaMa_Status","WaMa_Status" + WaMa_Status)
    if (Dose_WaMa_Power.state < 0.2) WaMa_Status.postUpdate("Aus")
    else if (Dose_WaMa_Power.state > 10) WaMa_Status.postUpdate("Läuft")
    else if (Dose_WaMa_Power.state < 4.5) {
        if (WaMa_Status.state == "Aus") WaMa_Status.postUpdate("Standby")
        else if (WaMa_Status.state == "Läuft") {
            finishLock.lock()
            try {
                Thread::sleep(10000) // Debounce for 10 seconds
                if (Dose_WaMa_Power.state < 4.5) WaMa_Status.postUpdate("Fertig!")
            } finally {
                finishLock.unlock()
            }
        }
    }
end

Has anyone find a good power consumption measuring that I can use in Australia.

Good project and do you think you could you the same idea on your metre box to tell you how much power is coming from fridge or the washing machine also on… Of course you couldn’t use a wall socket in the metre box.

@ adtwomey
I use plugwise for that.

https://www.plugwise.com/nl_NL/

what you use it with. I don’t know what you saying

Sorry…
This one I have bought 5 years ago… still working properly using a USB stick and 2 ‘plugs’
There is also a binding available. you can measure and switch on/off.
https://www.plugwise.com/en_US/products/plug

I read somewhere this also can be done by sonoff (Chinese stuff).

I like that tutorial a lot, unfortunately I’m not getting the expected results. I’m new at this which is why I suppose I’m making a beginners mistake. The only output I receive in my GUI is a “-”.

washingmachine.items:

String Washingmachine_OpState “Washingmachine State [MAP(washingmachine.map):%s]”

washingmachine.rules:

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3

rule “Washingmachine Consumption State Machine”
when
Item avmfritz_FRITZ_DECT_200_1_087610044241_power changed
then
if (avmfritz_FRITZ_DECT_200_1_087610044241_power.state < 0.2) Washingmachine_OpState.postUpdate(MODE_OFF)
else if (avmfritz_FRITZ_DECT_200_1_087610044241_power.state > 10) Washingmachine_OpState.postUpdate(MODE_ACTIVE)
else if (avmfritz_FRITZ_DECT_200_1_087610044241_power.state < 3) {
if (Washingmachine_OpState.state == MODE_OFF) Washingmachine_OpState.postUpdate(MODE_STANDBY)
else if (Washingmachine_OpState.state == MODE_ACTIVE) Washingmachine_OpState.postUpdate(MODE_FINISHED)
}
end

default.sitemap entry, embedded in a frame:

Text item=Washingmachine_OpState label=“Waschmaschine [MAP(washingmachine.map):%d]” icon=“washingmachine_2”

washingmachine.map:

0=Aus
1=Standby
2=Aktiv
3=Fertig
NULL=?
-=-

Some example lines from my measurements:

2019-02-10 15:58:39.185 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 394.390 W to 389.310 W
2019-02-10 15:58:54.815 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 389.310 W to 15.300 W
2019-02-10 15:59:09.967 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 15.300 W to 15.230 W
2019-02-10 15:59:24.975 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 15.230 W to 15.300 W
2019-02-10 15:59:39.805 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 15.300 W to 15.230 W
2019-02-10 15:59:54.821 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 15.230 W to 15.660 W
2019-02-10 16:00:10.474 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 15.660 W to 3.140 W
2019-02-10 16:00:24.803 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.140 W to 3.360 W
2019-02-10 16:00:54.825 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.360 W to 3.070 W
2019-02-10 16:01:25.005 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.070 W to 3.360 W
2019-02-10 16:01:39.833 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.360 W to 3.140 W
2019-02-10 16:01:54.927 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.140 W to 14.730 W
2019-02-10 16:02:10.041 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 14.730 W to 3.430 W
2019-02-10 16:02:24.927 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.430 W to 3.070 W
2019-02-10 16:02:54.887 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.070 W to 3.360 W
2019-02-10 16:03:24.864 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.360 W to 3.070 W
2019-02-10 16:03:54.877 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 3.070 W to 2.930 W
2019-02-10 16:04:09.840 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 2.930 W to 2.640 W
2019-02-10 16:04:39.825 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 2.640 W to 2.930 W
2019-02-10 16:04:55.208 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 2.930 W to 2.640 W
2019-02-10 16:05:24.834 [vent.ItemStateChangedEvent] - avmfritz_FRITZ_DECT_200_1_087610044241_power changed from 2.640 W to 2.930 W

Basic UI Output:
BasicUI-Output

May somebody please help me to find the problem?

You are defining the mapping in both your .items and your .sitemap. Also, in your .sitemap you are using a %d which is for a number, you have the correct %s in your .items definition.

1 Like

Thank you very much. It’s working fine now, but I still get the following warning in the logs:

17:13:41.396 [WARN ] [.rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state ‘0’ on item ‘Washingmachine_OpState’ with pattern ‘MAP(AVM.map):%d’: Cannot format state ‘0’ to format ‘%d’

It says “Cannot format state”, but still the “0” gets transformed into “Aus” as expected and described in my AVM.map file:

0=Aus
1=Standby
2=Active
3=Fertig
-=-
NULL=-

The result in the Basic UI is as expected:
image

For completeness, this is how my item looks like:

Number Washingmachine_OpState “Washingmachine State [MAP(AVM.map):%d]”

What am I doing wrong and how to avoid that warning?

You have to use %s instead.

Number Washingmachine_OpState “Washingmachine State [MAP(AVM.map):%s]”

trying to get this working here, but im not getting the MODE_FINISHED part.
It goes from off to on to off, but never to finished.

Hi,
could you share .items,.map and script?
i’ve tried this method but it hangs at STATE_DEBOUNCE and changing to STATE_FINISHED
this thread is very big and for someone new trying to find a decent working method it’s a mission.
the first method presented and extended one jumps from Active to Off and never Finished. i’m cursed :slight_smile:

Hello,
I’ve tried every solution given in this tutorial but for some reason i had no success. This could also be due to cheap “Tuya SP23” that loves to report 0W between the cycles when power drop from 1000w to 1w.
It may be me that didn’t have success with InfluxDb but in Grafana i can see everything.
Long story short…I took simplified version of the tutorial and i added timers to confirm if the power is constant or was just a spike, if it was a spike then the timer gets cancelled and is waiting for the next power drop.
the two variables on top represents the gaps between cycles.
this it works for me and i would like to share with others if helpful but i would like an opinion from someone with more experience if can be polished or it’s bad way of doing
I’ve left logInfo to help debug until timings are identified and working as intended
Washingmachine.rules

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3

val Washingmachine_Timer_1_Delay_sec  = 15
val Washingmachine_Timer_2_Delay_sec  = 21 //time off between cycles

var Timer Washingmachine_Timer_1       // timer < 0.5 W
var Timer Washingmachine_Timer_2       // timer < 3.5 W

rule "Washingmachine Consumption State Machine"
when
    Item Power4 changed
then
    if (Power4.state < 0.5){ 
        if (Washingmachine_Timer_1 === null) {
            Washingmachine_Timer_1 = createTimer(now.plusSeconds(Washingmachine_Timer_1_Delay_sec), [ |
                logInfo("Washingmachine Timer1", "Started < 0.5 W")
                if (Power4.state < 0.5){ 
                        Washingmachine_OpState.postUpdate(MODE_OFF) 
                        Washingmachine_Timer_1 = null  // Cancels the timer 1 when run out
                        logInfo("Washingmachine Timer1", "MODE_OFF < 0.5 W")
                }
                else if (Power4.state > 0.5){
                    if (Washingmachine_Timer_1 !== null) {  //// Cancels the timer 2 when Power4 > 3.5 W
                    Washingmachine_Timer_1.cancel()
                    Washingmachine_Timer_1 = null
                    logInfo("Washingmachine Timer1", "Canceled >0.5 W")
                    }
                }
            ])
            
        }
    }        
        
    else if (Power4.state > 3.5) {
                Washingmachine_OpState.postUpdate(MODE_ACTIVE)
                if (Washingmachine_Timer_1 !== null) {  //// Cancels the timer 1 when Power4 > 3.5 W
                    Washingmachine_Timer_1.cancel()
                    Washingmachine_Timer_1 = null
                    logInfo("Washingmachine Timer1", "Canceled > 3.5 W")
                }
                else if (Washingmachine_Timer_2 !== null) {  //// Cancels the timer 2 when Power4 > 3.5 W
                    Washingmachine_Timer_2.cancel()
                    Washingmachine_Timer_2 = null
                    logInfo("Washingmachine Timer2", "Canceled > 3.5 W")
                }

    }
    else if (Power4.state < 3.5) {
        if (Washingmachine_Timer_2 === null) {
            Washingmachine_Timer_2 = createTimer(now.plusSeconds(Washingmachine_Timer_2_Delay_sec), [ |
                if (Power4.state < 3.5) { 
                        logInfo("Washingmachine Timer2", "Started < 3.5 W")
                        if (Washingmachine_OpState.state == MODE_OFF){
                            Washingmachine_OpState.postUpdate(MODE_STANDBY)
                            Washingmachine_Timer_2 = null  // Cancels the timer 2 when run out
                            logInfo("Washingmachine Timer2", "Standby after MODE_OFF ")
                            }
                        else if (Washingmachine_OpState.state == MODE_ACTIVE){
                            Washingmachine_OpState.postUpdate(MODE_FINISHED) 
                            Washingmachine_Timer_2 = null  // Cancels the timer 2 when run out
                            logInfo("Washingmachine Timer2", "Finished after MODE_FINISHED ")
                            }
                    }
                else if (Power4.state > 3.5){
                    if (Washingmachine_Timer_2 !== null) {  //// Cancels the timer 2 when Power4 > 3.5 W
                    Washingmachine_Timer_2.cancel()
                    Washingmachine_Timer_2 = null
                    logInfo("Washingmachine Timer2", "Canceled >3.5 W")
                    }
                    else if (Washingmachine_Timer_1 !== null) {  //// Cancels the timer 1 when Power4 > 3.5 W
                    Washingmachine_Timer_1.cancel()
                    Washingmachine_Timer_1 = null
                    logInfo("Washingmachine Timer1", "Canceled > 3.5 W")
                    }  
                }
            ])
                
        }
    }
end

I have adapted the code to my hardware specifc (load levels and wait time to turn off if load is below 3 for two measurements in a row) but sometimes MODE_FINISHED is triggered for no apparent reason. Maybe you can figure this out.
Additional info: at other times during operation, the value dropped to 2 as well for a single measurement and did not cause the fals triggering of MODE_FINISHED.

Rule:

rule "Washingmachine Consumption State Machine (Extended)"
when
    Item SD_Jis1_Waschmaschine_Powerload changed
then
    if (SD_Jis1_Waschmaschine_Powerload.state > 20) SD_Jis1_Waschmaschine_Status.postUpdate(MODE_ACTIVE)
    else if (SD_Jis1_Waschmaschine_Powerload.state < 3) {
        if (SD_Jis1_Waschmaschine_Status.state == MODE_ACTIVE) {
            finishLock.lock()
            try {
                Thread::sleep(61000) // Debounce for 61 seconds
                if (SD_Jis1_Waschmaschine_Powerload.state < 3) {
                    SD_Jis1_Waschmaschine_Status.postUpdate(MODE_FINISHED)
                    SD_Jis1_Waschmaschine_Status.postUpdate(MODE_OFF)
                }
            } finally {
                finishLock.unlock()
            }
        }
    }
end

State values with wrong behaviour at 12:48:

2019-12-19 12:21:56.453 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 0 to 1
2019-12-19 12:48:57.438 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 1 to 2
2019-12-19 12:48:57.453 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 2 to 0
2019-12-19 12:48:57.500 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 0 to 1
2019-12-19 13:44:57.511 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 1 to 2
2019-12-19 13:44:57.523 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Status changed from 2 to 0

Powerload values at relevant time:

2019-12-19 12:44:56.436 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 115 to 2196
2019-12-19 12:45:56.419 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 2196 to 114
2019-12-19 12:46:56.435 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 114 to 2275
2019-12-19 12:47:56.401 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 2275 to 2
2019-12-19 12:48:57.479 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 2 to 102
2019-12-19 12:49:56.432 [vent.ItemStateChangedEvent] - SD_Jis1_Waschmaschine_Powerload changed from 102 to 95

Seems to be the expected behaviour; powerload was < 3 for more than 6100mS

But is this delay in value change somehow caused by the 61-second-sleep in the rule? For all other value changes the interval is exactly 60 seconds (and right after this it’s 59 seconds, so jumping back in the old rythm).

This isn’t a realtime system. Looking for a delay that you know will be around sixty seconds - and sometimes is known to vary to 59 seconds - using a 61 second threshold is dumb.

Yes, the long sleep in the rule may be contributing. It ties up system resources for the duration, so potentially could delay the processing of external events. There’d have to be other contributions as well - other rules with sleeps, lots of I/O activity, etc. - but not impossible.

1 Like

Do it without a Thread::Sleep and use your Persistence :slight_smile:

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3
rule "Waschmaschine Status"
when
    Item Steckdose_Waschmaschine_Power changed
then
    if (Steckdose_Waschmaschine_Power.state < 0.4) Waschmaschine_Status.postUpdate(MODE_OFF)
    else if (Steckdose_Waschmaschine_Power.state > 30) Waschmaschine_Status.postUpdate(MODE_ACTIVE)
    else if (Steckdose_Waschmaschine_Power.averageSince(now.minusMinutes(3))< 5){
      if (Waschmaschine_Status.state == MODE_OFF) Waschmaschine_Status.postUpdate(MODE_STANDBY)
      else if (Waschmaschine_Status.state == MODE_ACTIVE) Waschmaschine_Status.postUpdate(MODE_FINISHED)
    }
end
1 Like

Still have to get into the persistence topic as I didn’t need it so far.
I only use mapdb to store the last item state.

I use mapdb for restore at startup and influxdb for use cases like the washing machine.

rule "Waschmaschine Status"
when
    Item Steckdose_Waschmaschine_Power changed
then
    if (Steckdose_Waschmaschine_Power.state < 0.4) Waschmaschine_Status.postUpdate(MODE_OFF)
    else if (Steckdose_Waschmaschine_Power.state > 30) Waschmaschine_Status.postUpdate(MODE_ACTIVE)
    else if (Steckdose_Waschmaschine_Power.averageSince(now.minusMinutes(3), "influxdb")< 5){
      if (Waschmaschine_Status.state == MODE_OFF) Waschmaschine_Status.postUpdate(MODE_STANDBY)
      else if (Waschmaschine_Status.state == MODE_ACTIVE) Waschmaschine_Status.postUpdate(MODE_FINISHED)
    }
end
1 Like

I had difficulties to make the script work with a tplink 110 added in openhab2 through simple mode. The issue was the Unit “W” (defined as Number:Power tplinksmarthome_hs110_A8E90C_power “Power [%.1f %unit%]”).
The workaround was to change the checks as follow, adding "| “W”:
if (tplinksmarthome_hs110_A8E90C_power.state < 1 | “W” )
I got this solution through this post

hope this can help.
J