Washing Machine State Machine

Not really. I am always looking for ways to improve my code. So if I get a hint for something interesting, I am eager to try …

By the way: Not the subtraction is important to change. The most interesting facts are the methods toStandardHours, toStandardMinutes and toStandardSeconds of the Duration class. They are doing the math for me and my code can be reduced by some lines.

If have two datetime items, one for start time and one for finishing time, but currently i was not able to managed the calculation of the duration. In my simple minds it should be something like End-Start. So the tought about duration looks promising.

Thomas

Hey Thomas,

I am really sure you can calculate the duration with two items in the same way like I did. Do you want to give it a try? Here is an example:

[...]
        // get start and end from items
        val start = new DateTime(householdWashingMachineStart.state.toString)
        val end = new DateTime(householdWashingMachineEnd.state.toString)
        // calculate running time
        val seconds = (end.millis - start.millis) / 1000
[...]

Hey all,
I’m just about to follow the tutorial and read the whole thread. Very neat explanation. Thanks much!!!
I just found 2 small points mentioned in the conversations that are not reflected in the main tutorial yet.

1:

2:

Add the .map Example and explain the icon mapping also mentioning the limiations it has.
Again, thanks much for your efforts documenting this and helping other to improve their setup. :thumbsup:

Managed to pick up different readings for: off, cycle, pause, rinse and centrifuge, just with a clamp type amp meter (just a coil with an opamp it seems) and just when I was thinking, wouldnt it be nice if i could send all these states in my openhab washing channel, I came upon this post.
Thanks
Also thanks for the link to the icons. quite a few that come in useful

Thx all contributing to this thread - helped me a lot during the last days.
There I had some confusions changing the state model for a slightly different case, I swapped to a different pattern of coding.
I put the priority to the states and then evaluating thresholds.
I know it’ s not so efficient and contains more lines - but maybe it could help somebody else…

val Number STATE_OFF = 0
val Number STATE_STANDBY = 1
val Number STATE_ACTIVE = 2
val Number STATE_FINISHED = 3

val Number THRESHOLD_OFF = 5
val Number THRESHOLD_STANDBY = 20
val Number THRESHOLD_ACTIVE = 30

var java.util.concurrent.locks.ReentrantLock finishLock  = new java.util.concurrent.locks.ReentrantLock()

rule "Washingmachine_StateMachine"
when
    Item WashingMachine_Consumption changed
then
	switch WashingMachine_OpState.state	{
		case STATE_OFF: {
			 	if (WashingMachine_Consumption.state > THRESHOLD_STANDBY){
					WashingMachine_OpState.postUpdate(STATE_STANDBY)
				} 
				if (WashingMachine_Consumption.state > THRESHOLD_ACTIVE) {
					WashingMachine_OpState.postUpdate(STATE_ACTIVE)
				}
			}			
		case STATE_STANDBY: {
				if (WashingMachine_Consumption.state > THRESHOLD_ACTIVE) {
					WashingMachine_OpState.postUpdate(STATE_ACTIVE)
				}
				if (WashingMachine_Consumption.state < THRESHOLD_STANDBY) {
					WashingMachine_OpState.postUpdate(STATE_OFF)
				}
			}			
		case STATE_ACTIVE: {
				if (WashingMachine_Consumption.state < THRESHOLD_ACTIVE)	{
					finishLock.lock()
		            try {
		                Thread::sleep(10000) // Debounce for 10 seconds
		                if (WashingMachine_Consumption.state < THRESHOLD_ACTIVE) {
		                	 WashingMachine_OpState.postUpdate(STATE_FINISHED)
		               	} 
		            } finally {
		                finishLock.unlock()
		            }
				}
			}				
		case STATE_FINISHED: {
				if (WashingMachine_Consumption.state < THRESHOLD_STANDBY){
					WashingMachine_OpState.postUpdate(STATE_OFF)
				} 
				if (WashingMachine_Consumption.state > THRESHOLD_ACTIVE) {
					WashingMachine_OpState.postUpdate(STATE_ACTIVE)
				}	
			}		
		default: WashingMachine_OpState.postUpdate(STATE_OFF)
	}
end

I’m interested to hear your opinions…

5 Likes

I have done it in a similar way and i like your Threshold values. I will user it in my rule, makes it more readable.

Thanks
Thomas

Finally managed to finish this with my self built amp reader. Needs a bit of finetuning with regard to the amp values but works fine with Thom’s original (simple) rule.
But I did encounter an odd phenomenon when I add the state to items file: the group that I add it to, opens extremely slow.
I use
Number Washingmachine_OpState "Washingmachine State [MAP(was.map):%d]" <washingmachine> (GF_Garage, Machines)

where the map basically translates numbers 1-3 to words.
Anyway, if I remove the map command, it seems to go well.
So this one opens very very very slow (5-10 minutes)

But this one opens normally:

The only difference is the map file that looks like this:

0=OFF
1=Standby
2=Active
3=Finished
-=Error

That doesnt seem like a map that would take for ages tp load or execute.
Am I missing something?

Hey congrats on the first part.

The second part though… :confused: This is not normal and I’ve never experienced anything like it.

Hahaha, yes, several times :wink:

Maybe OpenHab is trying to teach me patience

Hmmm. Seems to affect the phone app much more than the basicUI.
restarting/clearing phone had no effect

Hi All,

this is defenitely a great thread about things weare able to do with openhab. I build a state machine as a rule in openHAB and it worked like a charm, but then i found the article about NodeRed. Thanks to @rgerrans

Because i heared often about NodeRed as one of THE IoT tools, i decided to give it a try. Installed NodeRed on an fresh openHABian installation (Thanks @ThomDietrich) and startet.

It is really simple to work with NodeRed just open the broswer an goto http://NodeRedServer:1880/ and start. I searched around and found the FSM node that represents a Finite State Machine in one node. Perfect, but as we know we have a little timer issue to overcome this jitter problem with many of the washing machines and therefore i could not use the FSM node.

Ok, i had to build the FSM with timer support on my own. The standard time in NodeRed could be retriggered, but not stopped, so i had to find a silution on this → stoptimer node did the trick for me.

At the end i created a flow that receives the power consumption from an item and sets the surrent sate of the washing machine using a String item.

For all who are interested the flow to import it into node red

[{“id”:“b6da215f.92716”,“type”:“tab”,“label”:“Washing Machine State Machine”},{“id”:“329acfe5.fbe06”,“type”:“openhab2-in”,“z”:“b6da215f.92716”,“name”:“WashingMachinePower”,“controller”:“e0c90f4f.513a8”,“itemname”:“WashingMachinePower”,“x”:120,“y”:600,“wires”:[[“172bb3e6.e94fcc”],]},{“id”:“172bb3e6.e94fcc”,“type”:“switch”,“z”:“b6da215f.92716”,“name”:“evaluate power”,“property”:“payload”,“propertyType”:“msg”,“rules”:[{“t”:“lt”,“v”:“2”,“vt”:“num”},{“t”:“lt”,“v”:“4”,“vt”:“num”},{“t”:“gt”,“v”:“10”,“vt”:“num”}],“checkall”:“false”,“outputs”:3,“x”:320,“y”:600,“wires”:[[“b5c4128b.3c3e3”],[“83b083d2.51a1e”],[“4e5a7235.047f3c”]]},{“id”:“1cf011b8.ce5d8e”,“type”:“switch”,“z”:“b6da215f.92716”,“name”:“evaluate state on power below 2W”,“property”:“payload.state”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“STATE_OFF”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_STANDBY”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_ACTIVE”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_FINISHED”,“vt”:“str”},{“t”:“else”}],“checkall”:“false”,“outputs”:5,“x”:860,“y”:340,“wires”:[[“c270265e.13e1f8”],[“c270265e.13e1f8”],[“2a38ca8.538d236”],[“c270265e.13e1f8”],[“c270265e.13e1f8”]]},{“id”:“b5c4128b.3c3e3”,“type”:“openhab2-get”,“z”:“b6da215f.92716”,“name”:“WashingMachineState”,“controller”:“e0c90f4f.513a8”,“itemname”:“WashingMachineState”,“x”:540,“y”:540,“wires”:[[“1cf011b8.ce5d8e”]]},{“id”:“35249b23.f02864”,“type”:“openhab2-out”,“z”:“b6da215f.92716”,“name”:“Set WashingMachineState”,“controller”:“e0c90f4f.513a8”,“itemname”:“WashingMachineState”,“topic”:“ItemUpdate”,“payload”:“”,“x”:1880,“y”:580,“wires”:},{“id”:“2a38ca8.538d236”,“type”:“stoptimer”,“z”:“b6da215f.92716”,“duration”:“150”,“units”:“Second”,“payloadtype”:“num”,“payloadval”:“0”,“name”:“”,“x”:1130,“y”:440,“wires”:[[“c270265e.13e1f8”],]},{“id”:“c270265e.13e1f8”,“type”:“change”,“z”:“b6da215f.92716”,“name”:“set STATE_OFF”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“STATE_OFF”,“tot”:“str”}],“action”:“”,“property”:“”,“from”:“”,“to”:“”,“reg”:false,“x”:1380,“y”:400,“wires”:[[“fc76d58.c9a6c28”]]},{“id”:“83b083d2.51a1e”,“type”:“openhab2-get”,“z”:“b6da215f.92716”,“name”:“WashingMachineState”,“controller”:“e0c90f4f.513a8”,“itemname”:“WashingMachineState”,“x”:540,“y”:600,“wires”:[[“ec78584d.73ef78”]]},{“id”:“ec78584d.73ef78”,“type”:“switch”,“z”:“b6da215f.92716”,“name”:“evaluate state on power below 4W”,“property”:“payload.state”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“STATE_OFF”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_STANDBY”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_ACTIVE”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_FINISHED”,“vt”:“str”},{“t”:“else”}],“checkall”:“false”,“outputs”:5,“x”:860,“y”:640,“wires”:[[“ac335bda.d55018”],[“ac335bda.d55018”],[“d4d30b61.0754d8”],[“e2463e68.5343d”],[“ac335bda.d55018”]]},{“id”:“ac335bda.d55018”,“type”:“change”,“z”:“b6da215f.92716”,“name”:“set STATE_STANDBY”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“STATE_STANDBY”,“tot”:“str”}],“action”:“”,“property”:“”,“from”:“”,“to”:“”,“reg”:false,“x”:1400,“y”:620,“wires”:[[“fc76d58.c9a6c28”]]},{“id”:“e2463e68.5343d”,“type”:“change”,“z”:“b6da215f.92716”,“name”:“set STATE_FINISHED”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“STATE_FINISHED”,“tot”:“str”}],“action”:“”,“property”:“”,“from”:“”,“to”:“”,“reg”:false,“x”:1400,“y”:520,“wires”:[[“fc76d58.c9a6c28”]]},{“id”:“d4d30b61.0754d8”,“type”:“stoptimer”,“z”:“b6da215f.92716”,“duration”:“150”,“units”:“Second”,“payloadtype”:“num”,“payloadval”:“0”,“name”:“”,“x”:1130,“y”:500,“wires”:[[“e2463e68.5343d”],]},{“id”:“4e5a7235.047f3c”,“type”:“openhab2-get”,“z”:“b6da215f.92716”,“name”:“WashingMachineState”,“controller”:“e0c90f4f.513a8”,“itemname”:“WashingMachineState”,“x”:540,“y”:660,“wires”:[[“2ee8f606.cf76fa”,“9a4f86d0.2018a8”]]},{“id”:“2ee8f606.cf76fa”,“type”:“switch”,“z”:“b6da215f.92716”,“name”:“evaluate state on power above 10W”,“property”:“payload.state”,“propertyType”:“msg”,“rules”:[{“t”:“eq”,“v”:“STATE_OFF”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_STANDBY”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_ACTIVE”,“vt”:“str”},{“t”:“eq”,“v”:“STATE_FINISHED”,“vt”:“str”},{“t”:“else”}],“checkall”:“false”,“outputs”:5,“x”:860,“y”:780,“wires”:[[“2f9848db.0c1c58”],[“2f9848db.0c1c58”],[“2f9848db.0c1c58”],[“2f9848db.0c1c58”],[“2f9848db.0c1c58”]]},{“id”:“9a4f86d0.2018a8”,“type”:“change”,“z”:“b6da215f.92716”,“name”:“send STOP”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“STOP”,“tot”:“str”}],“action”:“”,“property”:“”,“from”:“”,“to”:“”,“reg”:false,“x”:850,“y”:480,“wires”:[[“d4d30b61.0754d8”,“2a38ca8.538d236”]]},{“id”:“2f9848db.0c1c58”,“type”:“change”,“z”:“b6da215f.92716”,“name”:“set STATE_ACTIVE”,“rules”:[{“t”:“set”,“p”:“payload”,“pt”:“msg”,“to”:“STATE_ACTIVE”,“tot”:“str”}],“action”:“”,“property”:“”,“from”:“”,“to”:“”,“reg”:false,“x”:1400,“y”:720,“wires”:[[“fc76d58.c9a6c28”]]},{“id”:“7c4f6915.4e48e8”,“type”:“debug”,“z”:“b6da215f.92716”,“name”:“Debug Info”,“active”:true,“console”:“false”,“complete”:“payload”,“x”:1830,“y”:640,“wires”:},{“id”:“fc76d58.c9a6c28”,“type”:“rbe”,“z”:“b6da215f.92716”,“name”:“”,“func”:“rbei”,“gap”:“”,“start”:“”,“inout”:“out”,“x”:1670,“y”:580,“wires”:[[“35249b23.f02864”,“7c4f6915.4e48e8”]]},{“id”:“e0c90f4f.513a8”,“type”:“openhab2-controller”,“z”:“”,“name”:“openHAB”,“protocol”:“http”,“host”:“localhost”,“port”:“8080”,“path”:“”,“username”:“”,“password”:“”}]

Based on this experience, i this NodeRed is in minimum a perfect addon to the rule engine of openHAB and with openHABian it is a piece of cake to set it up.

Any comments, optimisations and and and are welcome.

Thomas

8 Likes

That looks really awesome. Thanks for sharing. :slight_smile:

Thanks for sharing @Dibbler42!

To be honest, I’m still not sure about NodeRED for my own use. Your picture looks cool for sure but is that supposed to be easier to write, read and modify than the ten lines of clear source code in my first posting? :upside_down:

2 Likes

@ThomDietrich

I’m not sure about that. For me it was a try and i transfered my “a little bite more than 10 lines” to node red just to try it. I hink it depends on programming skills and flow, rule or what ever to realise. A contact operated light is very simple to implement and leeds to quick results especially for beginners. But i am in doubt if it possible to tranfer my takerkoenig rule with item sorting and so on to node red.

Looking from my point it is annother option to work with and bring openHAB a little bit forward.

Thomas

That’s of course correct. That’s why we added NodeRED to openHABian and that’s why I’ve already linked your solution in the first posting here :wink:

1 Like

:+1:

I can´t read the power consumption, voltage etc. from a Sonoff POW flashed with Sonoff-Tasmota. The information page on the device says “Program version 5.0.6”

I have tried a few MQTT lines, such as:

Number nWama_Energy "Energy [%.1f]"  { mqtt="<[mosquitto:tele/sonoff-wama/ENERGY:state:default]" }

Number nWama_Energy "Energy [%.1f]"  { mqtt="<[mosquitto:tele/sonoff-wama/Power:state:default]" }

From the device console I see the following messages

01:10:22 MQTT: tele/sonoff-wama/STATE = {"Time":"2017-07-25T01:10:22", "Uptime":2, "Vcc":3.189, "POWER":"ON", "Wifi":{"AP":1, "SSID":"Home Zone", "RSSI":100, "APMac":"34:31:C4:5F:AC:8A"}}
01:10:22 MQTT: tele/sonoff-wama/ENERGY = {"Time":"2017-07-25T01:10:22", "Total":0.040, "Yesterday":0.015, "Today":0.025, "Period":2, "Power":30, "Factor":0.91, "Voltage":230, "Current":0.143}

I either see a dash or 0.0 as the item value.

Can somebody please give me a hint or share his items configuration?

Switch		box_heater				"Heater [%s]"				<radiator>	(gILeft)	{ mqtt="<[mqtt-loc:stat/box-heater/POWER:state:default],
																								<[mqtt-loc:tele/box-heater/STATE:state:JSONPATH($.POWER)],
																								>[mqtt-loc:cmnd/box-heater/power:command:*:default]"}
Number		box_heater_voltage		"Voltage [%d V]"			<energy>				{ mqtt="<[mqtt-loc:tele/box-heater/ENERGY:state:JSONPATH($.Voltage)]"}
Number		box_heater_current		"Current [%.3f А]"			<energy>				{ mqtt="<[mqtt-loc:tele/box-heater/ENERGY:state:JSONPATH($.Current)]"}
Number		box_heater_power		"Power [%.1f Wt]"			<energy>				{ mqtt="<[mqtt-loc:tele/box-heater/ENERGY:state:JSONPATH($.Power)]"}
Number		box_heater_today		"Today [%.3f kWt]"			<line>					{ mqtt="<[mqtt-loc:tele/box-heater/ENERGY:state:JSONPATH($.Today)]"}
Number		box_heater_yesterday	"Yesterday [%.3f кВт]"		<line>					{ mqtt="<[mqtt-loc:tele/box-heater/ENERGY:state:JSONPATH($.Yesterday)]"}

DateTime	box_heater_update		"Update [%1$ta %1$tR]"		<clock>					{ mqtt="<[mqtt-loc:tele/box-heater/STATE:state:JSONPATH($.Time)]"}
Number		box_heater_uptime		"Uptime [%d]"				<clock>					{ mqtt="<[mqtt-loc:tele/box-heater/STATE:state:JSONPATH($.Uptime)]"}
String		box_heater_state		"State [%s]"				<switch>				{ mqtt="<[mqtt-loc:tele/box-heater/STATE:state:JSONPATH($.POWER)]"}
Number		box_heater_rssi			"RSSI [%d %%]"				<signal>				{ mqtt="<[mqtt-loc:tele/box-heater/STATE:state:JSONPATH($.Wifi.RSSI)]"}
String		box_heater_lwt			"LWT [%s]"					<signal>				{ mqtt="<[mqtt-loc:tele/box-heater/LWT:state:default]"}

Thank you so much for sharing.
In order for that JSONPATH thing to work, I would need a rule, correct? If yes, would you also share your rule, please?

Rule is not necessary, you need to establish a transformation.