neilimixamo / Home-Assistant-Quick-Look-Mobile

118 stars 6 forks source link

Add Washing Machine Card #29

Closed Michelone86 closed 4 months ago

Michelone86 commented 4 months ago

Hi super @neilimixamo, I would like to use the EQUIPMENT tab to view the controls for my Washing Machine and Dryer (both Electrolux). Any ideas about it?

ps. I don't have other devices to insert in the EQUIPMENT tab, I just need to be able to control them. Thank you!

neilimixamo commented 4 months ago

Hi @Michelone86, since I don't have connected washing and dryer machines myself, could you tell me more about the brands/products you use?

Could you also detail the functions you would like to integrate into the cards (e.g., start a wash, stop it, check the status or its power consumption, etc.) as well as the style you prefer?

If you only want to display two cards in your equipment view, we could quadruple the card size, which would allow for more controls or information to be displayed.

There is also a nice existing 'Custom-card Washer' proposed by UI-Minimalist, what do you think about it ? We could draw inspiration from it and create a QLM card that integrates seamlessly with the rest of its theme.

image

Feel free to provide sketches or drawings of what you envision for greater clarity."

Michelone86 commented 4 months ago

Hello @neilimixamo, the card you proposed is really very interesting and would be great for me! The controls I would like to see are quite simple: Current State (on/off), Wash Phase, Pause, Resume, Remaining Time. I currently use this Electrolux integration https://github.com/albaintor/homeassistant_electrolux_status.

Thank you so much for the time you dedicate to this great work, you are really good 🙏😊

Michelone86 commented 4 months ago

Hi @neilimixamo, unfortunately I don't have that much programming skills and I can't figure out how to make this card work with my Electrolux appliances and your qlm. Could you help me? 🙏

neilimixamo commented 4 months ago

If you only need to see the washing phase, remaining time, and control start/pause actions for the machine, we can maintain a minimalist approach by using the basic QLM card model as shown in this example.

image

The progress state will be represented by the appropriate icon for each cycle phase and a slider, while the remaining time will be displayed as a label when the machine is running and will show "paused" when the machine is paused. Does this card meet your requirements?

Since I don't have access to such a device, I need the following information:

Michelone86 commented 4 months ago

Hi @neilimixamo , I'm so happy to hear that you can view and control my washing machine directly with your QLM! The example you showed me is really what I need!

Here is the data you requested:

Entity name: electrolux_lavatrice Entity name for starting: button.electrolux_lavatrice_executecommand_start Entity name for pausing: button.electrolux_lavatrice_executecommand_pause Entity name for resume: button.electrolux_lavatrice_executecommand_resume Entity name for washing cycle: sensor.electrolux_lavatrice_cyclephase and sensor.electrolux_lavatrice_cyclesubphase Entity name for remaining time: sensor.electrolux_lavatrice_timetoend

I also attach all the screenshots from the developers section. ps. I will also need to insert my dryer (electrolux), but I think I will be able to adapt your work without giving you further trouble ;) Lavatrice_1 Lavatrice_2 Lavatrice_3 Lavatrice_4 Lavatrice_5 Lavatrice_6 Lavatrice_7 Lavatrice_8 Lavatrice_9

neilimixamo commented 4 months ago

Ok, add first this new "appliance.yaml" beta template,

appliance (beta).yaml ```yaml appliance: template: - basic_card - badge_battery variables: entity: states: ["starting", "washing", "rinsing", "drying"] # add/adapt terms if needed label_on: 'Active' label_off: 'Inactive' styles: card: - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-background-active)'; } else { return 'var(--fan-background-inactive)'; } } else { return 'var(--fan-background-inactive)'; } ]]] custom_fields: slider_visible: card: type: custom:my-slider-v2 entity: | [[[ if (variables.entity) { return variables.entity; } else { return ''; } ]]] intermediate: true styles: card: - display: block - position: absolute - height: 100% - width: 100% - background-color: transparent - border-radius: 27px - box-shadow: none - bottom: 0vh - left: 0vw container: - width: 100% - height: 100% - position: absolute - bottom: 0vh - right: 0vw - overflow: hidden - border-radius: 27px track: - width: 100% - height: 100% - position: absolute - bottom: 0vh - right: 0vw - border-radius: 27px - background-color: | [[[ if (variables.entity) { var state = states[variables.entity].state.toLowerCase(); var percentage = states[variables.entity].attributes.percentage; if (state === 'on') { return 'var(--fan-slider-track-active)'; } } return 'var(--fan-slider-track-inactive)'; ]]] progress: - height: 100% - background: | [[[ if (variables.entity) { var state = states[variables.entity].state.toLowerCase(); var percentage = states[variables.entity].attributes.percentage; if (variables.states.some(s => state.includes(s.toLowerCase()))) { return 'var(--fan-background-active)'; } } return 'var(--fan-background-inactive)'; ]]] - border-radius: 27px 0px 0px 27px thumb: - width: 0px icon: card: icon: | [[[ if (variables.entity) { const state = states[variables.entity].state.toLowerCase(); if (state.includes('starting')) { return 'mdi:power'; } else if (state.includes('washing')) { return 'mdi:water'; } else if (state.includes('rinsing')) { return 'mdi:water-pump'; } else if (state.includes('drying')) { return 'mdi:weather-sunny'; } else if (state.includes('paused')) { return 'mdi:pause'; } else { return 'mdi:washing-machine'; } } else { return 'mdi:help'; } ]]] styles: card: - overflow: visible - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-icon-background-active)'; } else { return 'var(--fan-icon-background-inactive)'; } } else { return 'var(--fan-icon-background-inactive)'; } ]]] icon: - color: 'var(--fan-icon-active)' tap_action: action: toggle hold_action: action: none custom_fields: badge: type: custom:button-card # calls for the 'badge_battery' template name: card: label: | [[[ if (variables.entity) { if (states[variables.entity].state === "unavailable") { return 'Unavailable'; }else if (states[variables.entity].state === "paused") { return 'In Pausa'; } else { var state = states[variables.entity].state; if (state != 'off') { return states[variables.label_on].state; } else { return variables.label_off; } } } else if (variables.label) { return variables.label; } else { return 'Label'; } ]]] ```

then create the following card and give me feedback.

- type: custom:button-card
  template: appliance
  variables:
    entity: sensor.electrolux_lavatrice_appliancestate
    name: Lavatrice
    label_on: sensor.electrolux_lavatrice_timetoend
    label_off: 'Disattivata' 
    icon_tap_action:
      action: call-service
      service: button.press
      service_data:
        entity_id: >
          [[[
            if (states[variables.entity].state.toLowerCase() === 'off') {
              return 'button.electrolux_lavatrice_executecommand_start';
            } else if (states[variables.entity].state.toLowerCase() === 'paused') {
              return 'button.electrolux_lavatrice_executecommand_resume';
            } else {
              return 'button.electrolux_lavatrice_executecommand_pause';
            }
          ]]]

Can you also list the possible states of the entities sensor.electrolux_lavatrice_appliancestate, sensor.electrolux_lavatrice_cyclephase and sensor.electrolux_lavatrice_cyclesubphasecycle ?

Which entity will indicate "washing", "rinsing", "drying", "paused" ?

Michelone86 commented 4 months ago

Hi @neilimixamo, I created the board! The minutes remaining for the end of the cycle are correct. If I click on the label, the sensor.electrolux_lavatrice_appliancestate entity screen opens. If I click on the icon instead, I get the error "Service sensor.turn_off not found".

https://github.com/neilimixamo/Home-Assistant-Quick-Look-Mobile/assets/97887508/cf3d7e6c-8cef-478d-9cac-bfa824291be0

These are the states I was able to read since the last wash:

sensor.electrolux_lavatrice_appliancestate : off, running, end of cycle. sensor.electrolux_lavatrice_cyclephase : unavailable, wash, rinse, spin. sensor.electrolux_lavatrice_cyclesubphasecycle : not available, add garment, wash, rinse.

I await your instructions, thank you!

neilimixamo commented 4 months ago

This new code should fix the action bug and should adapt the icon and label according to the progress state of the washing machine, while displaying the remaining time in minutes.

Can you make sure that the entity 'sensor.electrolux_lavatrice_appliancestate' also indicates a 'paused' state?

appliance (beta 2).yaml ```yaml appliance: template: - basic_card - badge_battery variables: entity: label_on: 'Attivata' label_off: 'Disattivata' styles: card: - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-background-active)'; } else { return 'var(--fan-background-inactive)'; } } else { return 'var(--fan-background-inactive)'; } ]]] custom_fields: icon: card: icon: | [[[ const state = states[variables.entity].state.toLowerCase(); const phase = states['sensor.electrolux_lavatrice_cyclephase'].state.toLowerCase(); if (state === 'off') { return 'mdi:washing-machine-off'; } else if (state === 'running') { if (phase === 'wash') { return 'mdi:water'; } else if (phase === 'rinse') { return 'mdi:water-pump'; } else if (phase === 'spin') { return 'mdi:turbine'; } else { return 'mdi:washing-machine'; } } else if (state === 'end of cycle') { return 'mdi:check'; } else if (state === 'paused') { return 'mdi:pause'; } else { return 'mdi:help'; } ]]] styles: card: - overflow: visible - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-icon-background-active)'; } else { return 'var(--fan-icon-background-inactive)'; } } else { return 'var(--fan-icon-background-inactive)'; } ]]] icon: - color: 'var(--fan-icon-active)' custom_fields: badge: type: custom:button-card name: card: label: | [[[ const state = states[variables.entity].state.toLowerCase(); const phase = states['sensor.electrolux_lavatrice_cyclephase'].state.toLowerCase(); const timetoend = states['sensor.electrolux_lavatrice_timetoend'].state; if (state === 'off') { return variables.label_off; } else if (state === 'running') { if (phase === 'wash') { return 'Lavaggio - ' + timetoend + 'min'; } else if (phase === 'rinse') { return 'Risciacquo - ' + timetoend + 'min'; } else if (phase === 'spin') { return 'Asciugatura - ' + timetoend + 'min'; } else { return variables.label_on + ' - ' + timetoend + 'min'; } } else if (state === 'end of cycle') { return 'Finito'; } else { return 'Label'; } ]]] ```
- type: custom:button-card 
  template: appliance
  variables:
    entity: sensor.electrolux_lavatrice_appliancestate
    name: Lavatrice
    icon_tap_action:
      action: call-service
      service: button.press
      service_data:
        entity_id: >
          [[[
            const state = states[variables.entity].state.toLowerCase();
            const button = 'button.electrolux_lavatrice_executecommand_';

            if (state === 'off') {
              return button + 'start';
            } else if (state === 'paused') {
              return button + 'resume';
            } else {
              return button + 'pause';
            }
          ]]]

This template is currently not applicable to other devices but I'll try to make it more generic once we have finalized this card and as mentioned earlier, I cannot test this code myself, so I have to rely on your observations for the following improvements ;)

Michelone86 commented 4 months ago

Hi @neilimixamo , the code works perfectly! Is it possible to move the remaining minutes under the wash cycle name? Thank you! Screenshot_20240519_172151 Screenshot_20240519_172157 Screenshot_20240519_172235

neilimixamo commented 4 months ago

May I propose you one of the following alternatives instead of adding a third text line ?

image

Michelone86 commented 4 months ago

Ok the last solution seems the best to me, it would be perfect! (Replace state by name)

neilimixamo commented 4 months ago

Ok, let's try this then :

appliance (beta 3).yaml ```yaml appliance: template: - basic_card - badge_battery variables: entity: label_on: 'Attivata' label_off: 'Disattivata' styles: card: - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-background-active)'; } else { return 'var(--fan-background-inactive)'; } } else { return 'var(--fan-background-inactive)'; } ]]] custom_fields: icon: card: icon: | [[[ const state = states[variables.entity].state.toLowerCase(); const phase = states['sensor.electrolux_lavatrice_cyclephase'].state.toLowerCase(); if (state === 'off') { return 'mdi:washing-machine-off'; } else if (state === 'running') { if (phase === 'wash') { return 'mdi:hand-water'; } else if (phase === 'rinse') { return 'mdi:water'; } else if (phase === 'spin') { return 'mdi:turbine'; } else { return 'mdi:washing-machine'; } } else if (state === 'end of cycle') { return 'mdi:check'; } else if (state === 'paused') { return 'mdi:pause'; } else { return 'mdi:help'; } ]]] styles: card: - overflow: visible - background-color: | [[[ if (variables.entity) { const state = states[variables.entity].state; if (state !== 'off' && state !== 'paused') { return 'var(--fan-icon-background-active)'; } else { return 'var(--fan-icon-background-inactive)'; } } else { return 'var(--fan-icon-background-inactive)'; } ]]] icon: - color: 'var(--fan-icon-active)' custom_fields: badge: type: custom:button-card name: card: name: | [[[ const state = states[variables.entity].state.toLowerCase(); const phase = states['sensor.electrolux_lavatrice_cyclephase'].state.toLowerCase(); if (state === 'off') { return variables.name; } else if (state === 'paused') { return 'In Pausa'; } else if (state === 'running') { if (phase === 'wash') { return 'Lavaggio' ; } else if (phase === 'rinse') { return 'Risciacquo'; } else if (phase === 'spin') { return 'Asciugatura'; } else { return variables.name; } } else if (state === 'end of cycle') { return 'Finito'; } else { return 'Name'; } ]]] label: | [[[ const state = states[variables.entity].state.toLowerCase(); const timetoend = states['sensor.electrolux_lavatrice_timetoend'].state; if (state === 'off') { return variables.label_off; } else if (state === 'running') { return timetoend + ' min'; } else if (state === 'end of cycle') { return 'Finito'; } else { return variables.label_on ; } ]]] ```
Michelone86 commented 4 months ago

Ok, I did a quick test and it seems to work well! In the next few days I'll do some complete tests and update you, I'll wait to close the topic. Thanks Super @neilimixamo 🚀🚀🚀

Michelone86 commented 4 months ago

Hi @neilimixamo , I've been testing your card for a few days, everything works great and I even managed to get the dryer to work by adapting its entities based on your code!

Thank you so much, your QLM Dashboard is really perfect 😎🚀

Screenshot_20240521_122727

neilimixamo commented 4 months ago

Hi @Michelone86, I'm glad to hear that you achieved the dashboard you wanted. However, I notice that the cards are blue even though their status is "Spenta", they should be greyded instead, no?

Michelone86 commented 4 months ago

Hi @neilimixamo , yes the cards are Blue even if they are turned off... 🤔

Screenshot_20240521_133549

neilimixamo commented 4 months ago

Is the state of your inactive washing machine "off" or "end of cycle" ? Cause every other states than "off" or "paused" will return a blue card

Michelone86 commented 4 months ago

The state of the switched off washing machine is OFF.

neilimixamo commented 4 months ago

Can you try to change 'OFF' to 'off' in the dev tools to see if it fixes the problem ?

If it does, replace lines 15 and 59 of the appliance template with

if (state.toLowerCase() !== 'off' && state.toLowerCase() !== 'paused') {
Michelone86 commented 4 months ago

Hi @neilimixamo, I made the change you told me to do, now everything works correctly! Thank you ✌🏻 Screenshot_20240521_224257