Washing machine status widget

I have connected my Washingmachine to a plug that measures Wh. You can use any, i have one homematic and a tasmota. The tasmota has a bigger variance, i might replace it at some point. You need to monitor the consumption for the states and set the parameters in a rule. You also need to check how much timer you need in case the machine stops and starts the program again depending on the program. i am using 5 min. I am currently using the below rule, although i disabled the mode standby as this was not reliable and not needed as well. There is a pretty large thread here in the forum as well. Just look for washing machine…

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

var Timer tWashing_Machine = null

rule "Washingmachine Consumption State Machine"
when
    Item Washingmachine_Power changed
then
    if(!(Washingmachine_Power.state instanceof Number)) {
        logWarn("washing","Can't get valid power! {}",Washingmachine_Power.state)
        return;
    }
    
    val nPower = (Washingmachine_Power.state as Number).floatValue
//  logInfo("washing", "Washing Machine rule initiated.")

    if (nPower < 0.21 && Washingmachine_OpState2.state != MODE_OFF) { 

        Washingmachine_OpState2.postUpdate(MODE_OFF) 
        logInfo("washing", "Washing Machine OFF.") 

    } else if(nPower > 15 && Washingmachine_OpState2.state != MODE_ACTIVE) {

        Washingmachine_OpState2.postUpdate(MODE_ACTIVE)
        logInfo("washing", "Washing Machine ACTIVE.")

        if(tWashing_Machine !== null) {

            tWashing_Machine.cancel
            logInfo("washing", "Timer cancelled.")

        } else {

            logInfo("washing", "Timer not cancelled due to it being null.")

        }
    } else if (nPower < 3.5) {

//        if(Washingmachine_OpState2.state == MODE_OFF) {
//
//            Washingmachine_OpState2.postUpdate(MODE_STANDBY) 
//            logInfo("washing", "Washing Machine STANDBY") 
//
//        } else 
		
		if (Washingmachine_OpState2.state == MODE_ACTIVE) {

            if(tWashing_Machine === null) {

                logInfo("washing", "Timer created.")
                tWashing_Machine = createTimer(now.plusMinutes(5), [ |

                    Washingmachine_OpState2.postUpdate(MODE_FINISHED)
                    logInfo("washing", "Timer expired. Washing Machine FINISHED.")
                    tWashing_Machine = null

                ])
            }
        }
    }
end

Hi,
first off all awsome work on the widget and thank you for sharing. Implimenetation worked great just one part I am strunggling with:

State: Can be one of OFF, RUNNING or FINISHED and should be filled in via an expression as well depending on the current state of your machine

Can’t get the expression to work. I use an item to represent the status auf the washing machine (Washer_OpState_CE_Utilityroom). The state is given by a numeric value (0-3) that translate to/ repersent the staes: Off, Standby, Active and Finished.

Now my question, what expression to use to get the widget state to OFF, RUNNING, FINISHED? Maybe someone can point me in the right direction.

Thanks

i believe you are almost there. I also work with a numbers item that represents the state of my washing machine as you can also see in the rule i posted, so i changed the YAML file from @DrRSatzteil to replace the values OFF, RUNNING etc. with my numbers like this:

f7: '=items[props.state].state == "3" ? "circle_bottomthird_split" : "circle"'

1 Like

You can either do it like suggested by Jan or use an expression like this:

=items.washing_machine_state.state == 1 ? "OFF" : items.washing_machine_state.state == 2 ? "RUNNING" : "FINISHED"

Where washing_machine_state is the name of your status item.

1 Like

Thats it! Thank you very much …
… sometimes it is that little nudge and the help of others that put you back on track!

Is a rule like this not easier?
When: cron everyminute
But only if: Washingmachine_State == Running
Then: Washingmachine_Runtime.state + 1

And in your washingmachine_state rule add, when wassingmachine_state == OFF postUpdate(Washingmachine_Runtime, 0)

Yes I think your solution is simple and would work just like my more complicated rule. Thanks for sharing!

When you set the runtime to 0 is more a matter of taste in my opinion. I set it to 0 when the machine is starting so I am able to see how long the last washing was running after the washing is finished. However I agree that this information is pretty useless so it does not really matter

Thanks a lot @DrRSatzteil for this great Widget :slight_smile:

I use it with the Siemens/Bosch HomeConnect Binding.
I did some small modifications today to implement some parameters provided by the HomeConnect Binding for Washer and Dryer.

I replaced the ā€œdummyā€ buttons with the ā€œRemainingTimeā€ used from the binding.

The following parameters are added to your wonderful :slight_smile: widget as well.

  • Progress (used in a progress)
  • SpinSpeed
  • Temperatur
  • ProgramMode

When the Widget is set in ā€œDryerā€ mode, then the colors are changing from blue to red.

IMG_5924

uid: washing_machine_v3
tags: []
props:
  parameters:
    - description: Title of the card
      label: Title
      name: title
      required: false
      type: TEXT
    - description: Header text above washing machine
      label: Header
      name: header
      required: false
      type: TEXT
    - context: item
      description: Set Washer/Dryer Item (BSH HomeConnect)
      label: Washer/Dryer Item
      name: item
      required: true
      type: TEXT
    - description: Set On if Dryer is defined to change the color of Widged
      label: Item is Dryer?
      name: isDryer
      required: false
      type: BOOLEAN
  parameterGroups: []
timestamp: Mar 14, 2021, 6:59:23 PM
component: f7-card
config:
  title: =(props.title)
slots:
  default:
    - component: f7-card-content
      config:
        style:
          height: 175px
      slots:
        default:
          - component: f7-row
            config:
              class:
                - display-flex
                - justify-content-center
              style:
                width: 100%
            slots:
              default:
                - component: f7-col
                  config:
                    class:
                      - display-flex
                      - flex-direction-column
                      - align-items-center
                      - justify-content-space-evenly
                    style:
                      height: 100%
                      width: 90px
                  slots:
                    default:
                      - component: f7-block-header
                        slots:
                          default:
                            - component: Label
                              config:
                                text: =props.header
                      - component: f7-block
                        config:
                          style:
                            height: 120px
                            width: 90px
                            border-radius: 9px 9px 5px 5px
                            background: rgba(255,255,255,1)
                            box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px
                          class:
                            - display-flex
                            - flex-direction-column
                            - align-items-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                class:
                                  - display-flex
                                  - justify-content-space-between
                                  - align-items-center
                                  - flex-shrink-0
                                  - no-margin
                                style:
                                  height: 23px
                                  width: 85px
                                  padding-right: 5px
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      size: 10
                                      color: gray
                                  - component: f7-icon
                                    config:
                                      size: 10
                                      color: gray
                                  - component: Label
                                    config:
                                      text: =Math.floor(items[props.item + '_Remainingprogramtime' ].state.split(' ')[0] /3600) + ':' + (Math.floor(items[props.item +  '_Remainingprogramtime'].state.split(' ')[0] /60)  - Math.floor(items[props.item + '_Remainingprogramtime'].state.split(' ')[0] /3600)  * 60)
                                      visible: "=(items[props.item + '_Remainingprogramtime' ].state === 'UNDEF' || items[props.item + '_Remainingprogramtime' ].state === '0 s') ? false : true"
                                      style:
                                        font-size: 10px
                                  - component: f7-icon
                                    config:
                                      f7: "=(items[props.item + '_OperationState'].state === 'UNDEF' || items[props.item + '_OperationState'].state === 'Ready') ? 'circle_fill' : 'sun_min'"
                                      size: 10
                                      style:
                                        align-self: center
                                        color: "=(items[props.item + '_OperationState'].state === 'UNDEF' || items[props.item + '_OperationState'].state === 'Ready') ? 'gray' : 'green'"
                            - component: f7-progressbar
                              config:
                                progress: =Number.parseInt(items[props.item + '_ProgressState'].state)
                                infinite: false
                                style:
                                  --f7-progressbar-progress-color: "=Number.parseInt(items[props.item + '_ProgressState'].state) == 100 ? '#00ff00' : props.isDryer === true ? 'red' : '#179cd1'"
                                  --f7-progressbar-bg-color: "#ededed"
                                  --f7-progressbar-height: 2px
                                  width: 85px
                                  z-index: 999
                            - component: f7-block
                              config:
                                class:
                                  - display-flex
                                  - justify-content-center
                                  - align-items-center
                                style:
                                  height: 60px
                                  width: 60px
                                  border: 5pt solid lightgray
                                  border-radius: 50%
                                  margin-top: 7px
                              slots:
                                default:
                                  - component: f7-preloader
                                    config:
                                      size: 50
                                      colorTheme: "=props.isDryer === true ? 'red' : 'lightblue'"
                                      visible: =items[props.item + '_OperationState'].state == 'Run'
                                      style:
                                        position: absolute
                                  - component: f7-icon
                                    config:
                                      f7: "=items[props.item + '_OperationState'].state ==  'Finished' ? 'circle_bottomthird_split' : 'circle'"
                                      size: 50
                                      style:
                                        color: "=(items[props.item + '_OperationState'].state === 'UNDEF' || items[props.item + '_OperationState'].state === 'Ready') ? 'lightgray' : props.isDryer === true ? '#ff4f4f' : 'lightblue'"
                                        border-radius: 50%
                                        box-shadow: 0 0 16px 8px rgba(0, 0, 0, 0.25) inset
                                  - component: f7-block-header
                                    config:
                                      visible: =items[props.item + '_OperationState'].state == 'Run'
                                      class:
                                        - no-margin
                                      style:
                                        position: absolute
                                        text-align: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: "=items[props.item + '_SpinSpeed'].displayState === NULL ? items[props.item + '_SpinSpeed'].state.split('RPM')[1] : items[props.item + '_SpinSpeed'].displayState.split(' ')[0]"
                                            style:
                                              font-size: 11px
                                              color: black
                                        - component: Label
                                          config:
                                            text: "=items[props.item + '_WashingProgramTemperature'].displayState === NULL ? items[props.item + '_WashingProgramTemperature'].state.split('.')[4] : items[props.item + '_WashingProgramTemperature'].displayState"
                                            style:
                                              font-size: 11px
                                              color: black
                            - component: Label
                              config:
                                text: "=items[props.item + '_ActiveProgram'].state !== 'UNDEF' ? items[props.item + '_ActiveProgram'].state : ''"
                                style:
                                  font-size: 10px
                                  color: black

4 Likes

Hi,

I also can’t get the expression to work. I am very new to openHab and don’t have an idea how to implement the expression for OFF, RUNNING, etc.
I have the rule implemented that you posted above. This is working fine. I have an Item ā€œWaschmaschine_Statusā€ which gets values from 0-3.
I have also changed the YAML file as you described it.
But when I write Waschmaschine_Status in the state props field nothing happens.
Can you please give me a hint what I am doing wrong?
Thank you!

If you have already changed the yaml to work with values 0, 1, 2 and 3 you should set the expression to:

=items.Waschmaschine_Status.state

And you should be all set. Let me know if that works for you :blush:

Ok, now it works.
Thank you :slight_smile:

I added dryer mode as a small update in the first post :+1:

@muelli1967 could you explain what you have modified to show the widget in the rounded cell style?

I am using only these rounded cells, would be nice to add this widget in the same style as well.
Thanks!

I am away from my computer for a couple of days and will send it once back.

sorry guys, but I don“t get where I have to let the widget know what device/item I“m talking about. I can configure the states but still the widget won“t know which item it should watch.

edit: Ok, I found it…

I made an update to the first post: The look and feel of the washing machine will now be the same on android and iOS devices. Thanks goes to @Nico_R who created this custom component for the dishwasher status widget.

Hi, everyone.

I get the following error on the UI when trying to install this widget and the Dishwasher Status :

Installation of add-on 116621 failed

On the Karaf console I get the following:

12/26/2022 20:45:52.656 [ERROR] [mmunity.CommunityUIWidgetAddonHandler] - Widget from marketplace is invalid: Couldn't find the widget in the add-on entry

I installed other widgets from Rich Koshak and Thomas Lauterbach without problems.
I’m running OpenHAB 3.2 (stable) on a Raspberry Pi 4B with CentOS 8 (64bits).

Does anyone have any idea why this may happen?

1 Like

Maybe my post is in the wrong format!? The post is older than the community marketplace so this might be the case. I don’t find a lot of time during the holidays but I will check asap whether this could be the problem.

Could you please try again to install the widget from the marketplace? I just updated the first post so that it matches the required structure. Thank you!