Help with service_templates in javascript

Hi all,
I am trying to improve the functionality of the FLOORPLAN component by allowing service_templates. I want to be able to click on an element and have the action defined by a service_template. The original file ha-floorplan.html is here: ha-floorplan/www/custom_ui/floorplan at master · pkozul/ha-floorplan · GitHub

I have done some initial modifications as follows and I have access to the service_template BUT because I don’t know much about the backend of Home Assistant initially I had thought the service_template would already be parsed by the time it got to here but it isn’t. I then tried to replicate code I could see for a data_template but this is just giving me a script error. So the question is can someone point me in the direction of or provide a solution to what I need to add to get the service_template parsed to its final value? This is what is coming in as the service_template:

{% if is_state(‘light.meals_room’, ‘on’) %}turn_off{% else %}turn_on{% endif %}

This is where I am at the moment:

    onEntityClick(e) {
      e.stopPropagation();

      let entityId = this.entityId;
    
      let entityConfig = this.instance.entityConfigs[entityId];
      if (entityConfig.group.action) {
        let action = entityConfig.group.action;
        let service = action.service ? action.service : {};
        if (action.service_template) {
                this.instance.warn(action.service_template);
                let entities = this.instance.hass.states;
                let entityState = entities[entityId];
                let result = this.instance.assemble(action.service_template, entityState, entities);
                service = JSON.parse(result);
              }

        if (service) {
          let domain = action.domain ? action.domain : entityId.substr(0, entityId.indexOf('.'));
          domain = (domain == 'group') ? 'homeassistant' : domain;

          let data = {};
          if (action.data) {
            data = action.data;
          }
          if (action.data_template) {
            let entities = this.instance.hass.states;
            let entityState = entities[entityId];

            let result = this.instance.assemble(action.data_template, entityState, entities);
            data = JSON.parse(result);
          }

          if (!data.entity_id) {
            data['entity_id'] = entityId;
          }

          this.instance.hass.callService(domain, service, data);
        }
        else {
          this.instance.fire('hass-more-info', { entityId: entityId });
        }
      }
      else {
        this.instance.fire('hass-more-info', { entityId: entityId });
      }
    }

I just solved this by using a script

rear_hallway_lights_toggle:
  sequence:
    service_template: >
      light.{% if is_state('light.rear_hallway', 'on') -%}
        turn_off
      {% else -%}
        turn_on
      {% endif %}
    data: 
      entity_id: light.rear_hallway

and adding the script entity name to the floorplan and then in ui-lovelace.yaml adding

            - action:
                service: script.turn_on
              entities:
                - script.rear_hallway_lights_toggle
              name: Execute Scripts

If the goal is to toggle a light’s state then there’s an existing service to do that and it’s called light.toggle.

rear_hallway_lights_toggle:
  sequence:
    service: light.toggle
    data: 
      entity_id: light.rear_hallway

You can probably use it directly in the Lovelace code:

            - action:
                service: light.toggle
              entities:
                - light.rear_hallway
              name: Toggle Light

No the issue was there are a couple of rooms which have multiple bulbs grouped together. Occasionally these get out of sync and using toggle just results in them staying out of sync.This solution ensures that if any lights do stay on then another press on the floorplan will result in them turning off. If it happens when I turn them on then, yes, I will have to turn them all off and then turn them back on. I do have another page where all the bulbs are individually accessible but this will hopefully speed the process by allowing me to stay on my main screen.

All I’m saying is that this:

      light.{% if is_state('light.rear_hallway', 'on') -%}
        turn_off
      {% else -%}
        turn_on
      {% endif %}

is a long way of simply doing this:

light.toggle

No it isn’t.

If I have a group of 3 lights L1, L2 and L3 and L1 and L2 are ON and L3 is OFF then light.toggle results in L1 and L2 going off and L3 going on.

With my script above with L1, L2 ON and L3 OFF then calling the script results in them all turning off.

Edit: light.rear_hallway is a hue group of three lights.

Let’s review what your template does:

  • If light.rear_hallway is on the service call will be light.turn_off
  • If light.rear_hallway is off the service call will be light.turn_on

In other words, the service call always changes the light to the opposite of its current state. That’s exactly what light.toggle does.

Right, but if the light is off it doesn’t toggle to on if off was pressed.

All I see is a single Lovelace action that doesn’t distinguish between on and off. It simply toggles the state of light.rear_hallway (a light Hue group).

Yes, but you’re assuming the hardware does that :wink: I’ve had to do exactly the same thing with some of my hardware because its only a momentary switch.

It’s toggling a Light Group. How are light.turn_on and light.turn_off different than light.toggle in this situation?

EDIT

I’m wrong; it’s a Hue group.

The difference is if you call the script it will only toggle off the state. If you call the script, it will always toggle on the individual state, not the entire group.

current group state: on

You call either the script or toggle to turn it off:

grouped entity current state action result state desired
living room on script off yes
living room on toggle off yes
dining room off script off yes
dining room off toggle on no

I have a Hue group called light.dining_room consisting of two Hue lights.

Screenshot from 2020-10-05 10-36-29

I added this script:

  dining_room_toggle:
    sequence:
      service_template: >
        light.{% if is_state('light.dining_room', 'on') -%}
          turn_off
        {% else -%}
          turn_on
        {% endif %}
      data: 
        entity_id: light.dining_room

Using Developer Tools > Services, I called script.dining_room_toggle to turn the two lights on/off. I repeated the test with light.toggle. The lights behaved the same way; no difference.

Then I used light.turn_on to turn on just one of the two lights and compared the behavior of using the script versus light.toggle. The lights behaved the same way; no difference. If either light is on, both lights are turned off.

I encourage you to try it for yourself.

Seeing that you’re feverously typing away, i’ll off this explanation:

When any device is on, the overall state is on. I’ll call on = 1. Off will = 0

overall state individual state action individual state result
NA 1010101 toggle 0101010
1 1010101 smart_toggle 0000000
0 1010101 smart_toggle 11111111

See the results of my experiment above which demonstrated that there was no difference in behavior between using the script and light.toggle.

Let me know if my experiment was flawed and I can repeat the test using whatever conditions you believe will demonstrate that there is a difference.

That’s not how the group domain works.

Not disputing your experience but with my lights this doesn’t happen. If just one light is on and I call light.toggle then the light that is on goes off and the light that was off goes on. This is what @petro is trying to explain.

Petro’s explanation doesn’t describe my Hue group’s behavior. However, it does apparently describe your Hue group’s behavior. It’s this difference that interests me.

If you are also interested, and would like your Hue group to operate like my Hue group, I’m willing to compare notes so we can determine how our respective systems differ. On the other hand, if you prefer the way your Hue group behaves, then I guess we’re done here because I much prefer the way my Hue group responds to light.toggle.

I’d be willing to bet it’s hardware related (e.g. rev 1 vs rev 2). I have to have these ‘smart_toggles’ for old devices on my system. New devices are smart enough to understand grouping if they have associated devices.

FWIW, I’m using:

  • Philips Hue integration (version 0.115.5)
  • Second-generation Hue Bridge (square case)
  • Philips Hue White and color ambiance bulbs
  • Bridge and bulbs are running the latest firmware

The Hue integration’s ‘Allow Hue groups’ option is enabled.

Screenshot from 2020-10-06 10-17-34