custom-cards / button-card

❇️ Lovelace button-card for home assistant
MIT License
1.97k stars 242 forks source link

Trigger refresh based on time #436

Open slovdahl opened 3 years ago

slovdahl commented 3 years ago

Feel free to dismiss this if you feel it's out of scope for this custom card.

Is your feature request related to a problem? Please describe.

I have a button card for a switch entity where I want to show how long the switch has been on in the state_display. Showing the time was not hard, but getting it to refresh itself regularly isn't as easy. This is what I have at the moment:

  - type: 'custom:button-card'
    entity: switch.biltak_motorvarmare
    name: Motorvärmare
    icon: 'mdi:car'
    show_state: true
    state_display: |
      [[[
        if (entity.state === 'on') {
          function humanReadableDuration(duration) {
              var hours = Math.floor(duration / 1000 / 60 / 60);
              var minutes = Math.floor((duration / 1000 / 60 / 60 - hours) * 60);

              var result = '';
              if (hours > 0) {
                  result += hours + ' h ';
              }

              return result + minutes + ' min';
          }

          var timeDiff = new Date().getTime() - new Date(entity.last_changed).getTime();
          return 'Stäng av (på i ' + humanReadableDuration(Math.abs(timeDiff)) + ')';
        }
        else
          return 'Lägg på';
      ]]]

Describe the solution you'd like

Native support for refreshing a card regularly. I would like to e.g. trigger a refresh every 10th second.

I'm intentionally not getting into suggestion how it should be configured here.

Describe alternatives you've considered

It should be possible to create some kind of time entity myself, and add it to triggers_update, but for example the built-in Time & Date sensor does not support entities that updates more often than once a minute.

RomRider commented 3 years ago

I understand the use case, but let me ask you why not use show_last_changed: true instead? This refreshes the card automatically.

However, until a feature like this makes it officially into the card, there's a way to hack it to refresh on an interval. I'll put an example later, as I'm on my phone right now. 😊

slovdahl commented 3 years ago

I understand the use case, but let me ask you why not use show_last_changed: true instead? This refreshes the card automatically.

Nice, I had actually not seen that one 🙈 Thanks for pointing that out!

show_last_changed: true could work, although it doesn't do exactly the same thing. I'd prefer to have it part of the state (because I like to have some other text there as well), and I only want the time since last change to be shown for one specific state. I suppose it could be possible to hack it with CSS to be shown on the same line as the state, and possibly only during a specific state as well.

So natively would still be the perfect way in this case, but I'll just go ahead and create a time sensor if you don't feel this is worthwile adding. It just felt like this could be something that others would be interested in as well. Anyway, waiting for your hack example as well 👍🙇

slovdahl commented 3 years ago

And to illustrate the use case even more, it's a switch for a car engine pre-heater. So that's why the time since last change is only relevant when it's on. First one says "Turn on", and the second says "Turn off (on for x minutes)".

Skärmavbild 2021-02-21 kl  18 46 20 Skärmavbild 2021-02-21 kl  18 47 37
RomRider commented 3 years ago

That should work (it's a bit hacky, but who cares 😊 ):

  - type: 'custom:button-card'
    entity: switch.biltak_motorvarmare
    name: Motorvärmare
    icon: 'mdi:car'
    show_state: true
    state_display: |
      [[[
        if (entity.state === 'on') {
          if (this._myTimer === undefined) {
            this._myTimer = window.setInterval(() => { this.update() }, 1000 * 10) // That value 10*1000 is 10000 ms = 10 sec
          }
          function humanReadableDuration(duration) {
              var hours = Math.floor(duration / 1000 / 60 / 60);
              var minutes = Math.floor((duration / 1000 / 60 / 60 - hours) * 60);

              var result = '';
              if (hours > 0) {
                  result += hours + ' h ';
              }

              return result + minutes + ' min';
          }

          var timeDiff = new Date().getTime() - new Date(entity.last_changed).getTime();
          return 'Stäng av (på i ' + humanReadableDuration(Math.abs(timeDiff)) + ')';
        }
        else {
          if (this._myTimer !== undefined) {
            window.clearInterval(this._myTimer);
            delete this._myTimer;
          }
          return 'Lägg på';

        }
      ]]]

Edit: some code update

slovdahl commented 3 years ago

Nice, thanks a lot! I'll definitely try this one out. Will this work when defined in a button card template as well? 🤔 I mean, this should refer to the actual card the template is used in, shouldn't it?

RomRider commented 3 years ago

It refers to the button-card DOM element, it's unrelated to templates, you can put that anywhere ;)

slovdahl commented 3 years ago

Works very well, awesome. I'll live with this until or if the native support is ever added 👍🙇

vring0 commented 2 years ago

+1 I need dynamic map update support too. I use this when I launch animated icons when clicking on the map.