Trying to use template conditional to set variable in script

Ok, so I kinda get the idea behind {{ templates }} and how to use {% if %} and {% else %}, but I can’t quite figure out how to put it into use.

I’m trying to code a [Harmony] remote control using a picture-entity card, with each button calling a script. I need to send different codes depending on if you’re watching TV or using the Roku. I can get that info by checking the current_activity state of the Harmony remote.

Here’s my grand plan. (I couple implement this in moments in Python; YAML is a different beast)

This is pseudo-code, so ignore formatting and specific syntax.

The key element is the device code, an 8-digit number that identifies which device the command is targeted at: the Roku, or the Xfinity DVR.


{{ if messy.get.remote_mode.state == 'Watch Roku' }}
    {{ set device_id = "12345678" }}   <---------
{{ else }}
    {{set device_id = "87654321" }}    <---------

script:
  fastforward:
     sequence:
        service: remote.send_command
        entity: remote.harmony_remote
        data:
           entity_id: remote.harmony_remote
           device: {{ device_id }}       <----------
           command:
             - FastForward

(and likewise for a dozen or more script commands)

The “commands” are almost always identical, but the device code changes.

Is it possible to do this? Can someone help me with the actual syntax?

Thanks in advance.

Not sure you need scripts here; I would think you could just do this directly in the action part of an automation. But, nevertheless, if you do want to call the remote.send_command service from within a script, then it looks like what you’re trying to do is to figure out how to send the device_id to the script and use it from within the script. That basically looks like this (also sending the entity_id as well):

- service: script.fastforward:
  data:
    entity_id: remote.harmony_remote
    device_id: '12345678'
script:
  fastforward:
    sequence:
      service: remote.send_command
      data_template:
        entity_id: "{{ entity_id }}"
        command: FastForward
        device: "{{ device_id }}"

Of course you could also send the command if you wanted.

Basically, the script is called sending the entity_id and device_id as variables. Inside the script those variables will exist and can be used within templates.

Did I understand correctly what you’re trying to do? Will this work for you do you think?

Okay, so I’ve got the template-variable part right - right? I’ll just have to sort out the exact syntax (it’s kinda ugly and full of mustaches).

Some of the remote buttons need to be scripts/automations because it takes more than one Harmony command to make them work. For example, I’ve got a row of favorite channels. I wanted them to switch to TV mode no matter what mode you were in - Roku, Xbox, etc. So those have to send the “start activity” for Watch TV, and then “dial” the 3-digit channel number plus “Select” into the DVR.

It’s helpful that many of the common functions have the same command name. In TV World and in Roku World, the down-arrow command is DirectionDown for both devices. It’s just the device code that changes.

Let me re-read your notes and post a follow-up.

If you’re curious, this is what it looks like (or looked like about a week ago; I’ve had to make some changes.)

Screenshot_20181111_062010

Thanks for the advice.

I’m resurrecting this after a few days because I still haven’t puzzled it out.

I’ve got the {{ template }} if/else written correctly (tested it in the dev tools), but I still can’t sort out how to implement it.

If I set it up as a script to check the state of the remote, I’ll have to call that script each time a button is pressed.

I spent some time (too much time) this afternoon reading about automations and templates and conditions and sensors.

It doesn’t seem like this should be difficult.

An automation seems to make the most sense, since (to the best of my understanding) it sits there and waits for something to change. I should be able to wire it up to watch the attribute current_activity of the harmony remote and set my device_id accordingly. But I can’t figure out how or where to store an arbitrary value.

Experienced HA’ers… what am I missing?

Normally, I don’t think you can get around calling the script to do that check every time as you can’t store arbitrary values long-term from within the YAML. There are workarounds for storing values, e.g. writing values to a template sensor, writing to a file, etc. but then you have to check those every time anyway.

If you really want to be able to store it as a variable when it changes and then reference that later when the button is pressed, you could consider running your automations with AppDaemon or Node-RED.

Personally, I’d just do the state check and forward it onto another script like pnbruckner mentioned. You should be able to come up with a chain of them so that you only have to write the check template in one place and pass that to another script to use.

Thanks for the ongoing analysis.

I’ve kinda been looking for a reason to explore AppDaemon… I think I just found it. I’m pretty fluent in Python. But YAML, I think I hate. It works, it’s not as cluttered as JSON, but…

Why do you say that? You can certainly store values – in an input_number, an input_text, etc. These values are event save/restored through restarts.

@bundito, I can help you do what you want in YAML, but I’m not familiar with harmony remotes. You’re going to have to describe better what entities exist, what their attributes look like, how you detect when a button is pushed (i.e., exactly what changes), etc. I suspect all you need (possibly for each button) is an automation that waits for the event that indicates a button has been pushed and then uses the appropriate services to send the commands you want. Scripts might make it easier, but that’s yet to be seen. FWIW, I’m not following why you think you have to store something.

@pnbruckner - thanks for the offer. I’m going to try applying AppDaemon to this problem. Another learning opportunity.

The main problem is this… when you use remote.send_command for a Harmony remote, one of the data values you have to send is a “device ID” that dictates which of your devices the command should affect. Remember programming a universal remote and typing in those command codes? It’s like that, but the Harmony setup does it automatically. But you still have to send the right code for the right device.

The problem is that I have a Roku TV and an Xfinity DVR. They have different device ID’s. So buttons like “Up”, “Down”, “Select”, etc., are used in both scenarios (“Watching Roku” or “Watching TV”). But I have to transmit “Up” along with the correct device ID depending on the scenario.

In HA, the Harmony remote has an attribute that will tell me which activity is active: Roku or TV. So based on that, I could (theoretically) set a DEVICE_ID variable for Roku or for TV. And that way, my buttons would use DEVICE_ID to send the right command to the right device.

Does it make sense now? (I know it’s confusing, trust me.)

You wouldn’t need to store it. You could just use a data_template. I don’t think you provided the details of how you tell which activity is active, but you could just do something like:

service: remote.send_command
data_template:
  ...
  device: >
    {% if state_attr_is('remote.XXX', 'ATTR', 'ATTR_VALUE') %}
      12345678
    {% else %}
      87654321
    {% endif %}

EDIT: Or you could use that to decide which device_id to pass into a sequence if you had to send more than one command.

1 Like

That’s almost letter-for-letter correct. I believe it’s:

{% if state_attr_is('remote.harmony_hub', 'current_activity', 'ATTR_VALUE') %}

And ATTR_VALUE will be set to “Watch TV”, “Watch Roku”, “Play Xbox”, or ‘Play PS4’

Note: I don’t care about the arrows for the Xbox or the PS4, so don’t panic. I don’t think they have any real use, not when you have a gaming controller in your hand.

Thanks for all the help. I’m a reasonably experienced coder (I’m a developer for KDE as well), but this Home Assistant stuff is entirely new. It’s remarkably extensible and fully-featured, but it’s not simple. Setting HA has been on my to-do list for a few years. Now I’m elbows-deep in it and not working on anything else. :sunglasses:

@pnbruckner - I finally got around to trying this, and it works like a champ. One of my finished scripts looks like this:

right:
  sequence:
  - service: remote.send_command
    data_template:
      device: >
        {% if is_state_attr('remote.harmony_hub', 'current_activity', 'Watch TV') %}
          41961818
        {% else %}
          47477824
        {% endif %}
      entity_id: remote.harmony_hub
      command: DirectionRight

It works great and I could even add `{% elif %} sections if I wanted to control my other devices. Thanks for explaining the data template. Good stuff.

However… I’ve got another problem. You know the central “Ok” button on your remote? I have to send different commands in TV mode and in Roku mode. The cable box expects the command “Select”; the Roku expects the command “Ok”.

How would you go about combining these? I tried this:

select:
   sequence:
   - service: remote.send_command
     data_template:
       device: >
         {% if is_state_attr('remote.harmony_hub', 'current_activity', 'Watch TV') %}
           {% set button="Select" %}
           41961818
         {% else %}
           {% set button="Ok" %}
           47477824
         {% endif %}
       command: >
         {{ button }}
       entity_id: remote.harmony_hub

… thinking I could kill two birds with one conditional. It works fine in the template tester, but not in real life. There’s no error raised, but it seems to blow right past the command entry.

Any further guesses?

Why not create a basic script that maps your device_id’s to the activity. Then make scripts that call the activities:

Map inside harmony.conf

  26592473 - Switch
  -1 - PowerOff
  24089909 - PS4
  24103421 - Xbox One
  24090202 - Wii U
  24090353 - PC
  30947237 - TV

so after removing poweroff your script that handles calls would be:

script:
  harmony_command:
    sequence:
      - condition: template
        value_template: >
            {% set mapper = {
              'Switch': '26592473',
              'PS4':'24089909',
              'Xbox One':'24103421',
              'Wii U':'24090202',
              'PC':'24090353',
              'TV':'30947237'} %}
            {{ device in mapper }}
      - service: remote.send_command
        data_template:
          entity_id: remote.harmony_remote
          device: >
            {% set mapper = {
              'Switch': '26592473',
              'PS4':'24089909',
              'Xbox One':'24103421',
              'Wii U':'24090202',
              'PC':'24090353',
              'TV':'30947237'} %}
            {{ mapper[device] }}
          command: "{{ command }}"

And individual scripts would be:

  fastforward:
    sequence:
      - service: script.harmony_command
        data_template:
          device: "{{ state_attr('remote.harmony_hub','current_activity') }}"
          command: FastForward

That’s a pretty cool solution. Maps are perfect for this situation.

I’m having a hard time finding out all the ways you can embed “programming” into a YAML file. I’ve only used YAML as a data-storage format.

Are these things like templates a natural part of YAML, or do they come from HA?

they come from HA. It’s not a natural part of yaml. The language is Jinja and it’s very similar to python but it has limitations. The looping scope is a big one. I.E. a simple counter is not possible because you cannot declare a variable outside a loop and adjust it inside the loop.

Yaml is like JSON or XML. It’s just a formatting language. Yaml’s whole trope was to be like python; human readable and whitespace driven.

Just a small improvement to Petro’s script. You don’t need to add the collection map twice and it looks better when defined in yaml IMO:

script:
  harmony_command:
    sequence:
      - variables:
          mapper:
            Switch: '26592473'
            PS4: '24089909'
            Xbox One: '24103421'
            Wii U: '24090202'
            PC: '24090353'
            TV: '30947237'
      - condition: template
        value_template: {{ device in mapper }}
      - service: remote.send_command
        data_template:
          entity_id: remote.harmony_remote
          device: {{ mapper[device] }}
          command: "{{ command }}"

Missing mapper variable

Good catch! I have fixed it. Thanks

1 Like