Washing Machine State Machine

Hey Christoph,
would you like me to add your report to the first posting (with contribution of course)?

I have no polished solution at hand but there sure is a better way to calculate the difference between two timestamps. You are using joda DateTime, how about Duration?

Hey ThomDietrich,

[quote="ThomDietrich, post:101, topic:15587]
would you like me to add your report to the first posting (with contribution of course)?[/quote]
thanks for asking. I am feeling honored. Why not, feel free to add my post to your tutorial.

[quote="ThomDietrich, post:101, topic:15587]
You are using joda DateTime, how about Duration?[/quote]
I am going to test using Duration Class for my calculations and give you feedback.

Hey ThomDietrich,

I tried to use Duration for my calculations but I think it is not possible to use it in rules. My test rule looks like this:

        // get last change from item
        val lastChange = new DateTime(householdWashingMachineLastChange.state.toString)

        // calculate duration
        val duration = new Duration(lastChange.millis, now.millis)
        logInfo("household", duration)

        // calculate running time
        val seconds = (now.millis - lastChange.millis) / 1000

Triggering the rule results in following error message:

2017-04-27 11:49:51.357 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Washing Machine State changed - TEST': An error occured during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.LogAction.logInfo(java.lang.String,java.lang.String,java.lang.Object[]) on instance: null

I also tried

val duration = new Duration(lastChange.getMillis(), now.getMillis())

with the same result as above and

val duration = now.millis.minus(lastChange.millis())

with different error message

2017-04-27 11:58:30.127 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Washing Machine State changed - TEST': An error occured during the script execution: The name '<XMemberFeatureCallImplCustom>.minus(<XMemberFeatureCallImplCustom>)' cannot be resolved to an item or type.

Any ideas?

Is there some reason that you are avoiding the simple minus operator, as shown in your own post?

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.


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.



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.
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_ACTIVE = 30

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

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

I’m interested to hear your opinions…


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.


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:


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.



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:



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.


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