button-card icon indicating copy to clipboard operation
button-card copied to clipboard

Service call inside state display

Open arifroni opened this issue 2 years ago • 2 comments

Hello

I want to create an update card, where I want to display the version name and update button beside it. when I click the button I want it to update the entity. (example code with a light on service)

image

- type: custom:button-card
  variables:
    updates: sensor.template_updates
    other_updates: sensor.template_other_updates
    hacs_installed: sensor.hacs_installed
  show_state: true
  show_name: false
  show_icon: false
  styles:
    state:
      - text-align: left
      - justify-self: left
      - white-space: normal
    card:
      - cursor: default
      - padding: 0.2em 0 0 0.6em
      - letter-spacing: var(--mdc-typography-body1-letter-spacing)
  tap_action:
    href:
  state_display: |
    [[[
      // Function to call a service
      function callService() {
        hass.callService('light', 'turn_on', { entity_id: 'light.office_group_1' });
      };
      
      // variables
      let output = '',
          updates = states[variables.updates],
          hacs_installed = states[variables.hacs_installed]?.attributes.repositories,
          //other_updates = states[variables.other_updates],
          hacs_update = states['update.hacs_update']?.attributes.installed_version,
          no_updates = variables.translate_no_updates,
          update_available = variables.translate_update_available,
          updates_available = variables.translate_updates_available;
      
      const rename = a => {
          if (a.friendly_name && a.friendly_name.match(/update/i)) {
              const nameWithoutUpdate = a.friendly_name.replace(/update/i, '').trim();
              return nameWithoutUpdate;
          } else {
              return a?.friendly_name;
          }
      };
      
      // update entities
      Object.keys(states).forEach(key => {
        let s = states[key], e = s.entity_id, a = s.attributes;
        if (e.includes('update.') && !e.includes('update.home_assistant') && s.state === 'on') {
          let releaseLink = a.release_url ? `<a href="#" onclick="window.open('${a.release_url}');">${rename(a)}</a>` : rename(a);
          output += `
            <li>
              <b>${releaseLink}</b>
              ${a.installed_version} <b>&rarr;</b> ${a.latest_version}
              <button class="service-button" onclick=callService()>Update</button>
            </li>`;
        }
      });

      // subtitle
      let count = updates?.attributes.update_entities,
          subtitle = count === 0
              ? `${no_updates} <b>&larr;</b> ${hacs_update || ''}`
              : `${count} ${count === 1 ? update_available : updates_available} ${String.fromCodePoint('0x1f389')}`;

      return `
        <ha-icon icon="mdi:package-up"></ha-icon> <span class="title">Integrations</span><br>
        <p class="subtitle">${subtitle}</p>
        <ul>${output}</ul>
      `;
    ]]]

in the consol I get error

Uncaught ReferenceError: callService is not defined
    at HTMLButtonElement.onclick (VM4910 0:1:1)

can it be done?

arifroni avatar Aug 22 '23 12:08 arifroni

Have you managed this to work?

pickonedev avatar Mar 09 '25 08:03 pickonedev

Have you managed this to work?

no 😔

arifroni avatar Mar 09 '25 10:03 arifroni

Try by putting it as an anonymous function directly in onClick.

<button class="service-button" onclick="(function(){ console.log('Replace me') }()">Update</button>

RomRider avatar Aug 13 '25 12:08 RomRider

Alternative which should work: assign the function to a variable. Usually assigning to window is best. e.g. window.bcCallService = (hass) => { ... };. Then in your event handler use onClick'"window.bcCallService(this.hass)"

dcapslock avatar Aug 19 '25 08:08 dcapslock

thanks. this works

      // update entities
      Object.keys(states).forEach(key => {
        let s = states[key], e = s.entity_id, a = s.attributes;
        if (e.includes('update.') && !e.includes('update.home_assistant') && s.state === 'on') {
          let releaseLink = a.release_url ? `<a href="#" onclick="window.open('${a.release_url}');">${rename(a)}</a>` : rename(a);
          output += `
            <li>
              <b>${releaseLink}</b>
              ${a.installed_version} <b>&rarr;</b> ${a.latest_version}
              <button class="service-button" onclick="window.bcUpdate('${e}')">Update</button>
            </li>`;
        }
      });

      // subtitle
      let count = updates?.attributes.update_entities,
          subtitle = count === 0
              ? `${no_updates} <b>&larr;</b> ${hacs_update || ''}`
              : `${count} ${count === 1 ? update_available : updates_available} ${String.fromCodePoint('0x1f389')}`;
      window.bcUpdate = (entity) => hass.callService('update','install', { entity_id: entity });
      return `
        <ha-icon icon="mdi:package-up"></ha-icon> <span class="title">Integrations</span><br>
        <p class="subtitle">${subtitle}</p>
        <ul>${output}</ul>
      `;
    ]]]

arifroni avatar Aug 19 '25 09:08 arifroni

since there is huge momentum now....

and these type of configs are not wide-spread: would it be feasible to add to the wiki some of these snippets? So we can find use cases we didnt think of ourselves ;-)

example:

<button class="service-button" onclick="window.bcUpdate('${e}')">Update</button>

example: I hadn't found the use of the button class itself, let alone adding the onclick to it...

would be cool to have all to those listed cleanly

Mariusthvdb avatar Aug 19 '25 09:08 Mariusthvdb

Show and tell discussion would be suitable place for highlighting solutions like this.

dcapslock avatar Aug 19 '25 09:08 dcapslock

Good idea! I will post there.

arifroni avatar Aug 19 '25 09:08 arifroni