Nerwyn / android-tv-card

Universal Customizable TV Remote Card, with HA actions, super configurable touchpad, slider, haptics, and keyboard
Apache License 2.0
183 stars 9 forks source link

Templates like button-card #91

Closed Javerre closed 3 weeks ago

Javerre commented 1 month ago

Is your feature request related to a problem? Please describe. I wish to use your excellent remote card to control an LG WebOS TV. The integration for that does not have a “remote” entity, but a “media_player” entity. Furthermore all key presses must call the “webostv.button” service. I would also like to disable the buttons when the TV is off.

All this can already be done using android-tv-card but it requires a huge amount of configuration, which is then fragile and error prone.

Describe the solution you'd like I would like you to implement button templates to simplify the configuration YAML. Take a look at how this is done in the button-card plugin, which I have found to be very effective and concise.

I think the best way to do this would be to introduce “template” and “variable” options for each button in the “custom_actions:” section. The “template” option would refer to action templates a globally defined action template where the details for button actions can be specified once, parametrised by any values set in the “variables” option. For example:

custom_actions:
  left:
    template: webostv
    variables:
      - command: LEFT
  right:
    template: webostv
    variables:
      - command: RIGHT
action_templates:
 webostv:
    tap_action:
      haptics: {{ is_state(‘media_player.lg_smart_tv’, ‘on’) }}
      action: |
        {% if is_state(‘media_player.lg_smart_tv’, ‘on’) %}
          call-service
        {% else %}
          none
        {% endif %}
      service: webostv.button
      target:
        entity_id: media_player.lg_smart_tv 
      data:
        command: {{ variables.command }}

Describe alternatives you've considered I can implement a lot of the functionality I need using “button-card” but I would lose the touchpad feature which is what I really want. Alternatively I can just grit my teeth and copy/paste/edit a couple of hundred lines of YAML and use android-tv-card.

Additional context I think this would greatly enhance the functionality of android-tv-card and reduce the demand to add support for new integrations. Furthermore, no change to core functionality would be required, just to the code that reads and interprets the configuration.

Nerwyn commented 1 month ago

Alternatively, I can introduce just the template field to custom actions and make it so that any custom action can be used as a template using this deep merge function:

custom_actions:
 webostv:
    tap_action:
      haptics: {{ is_state(‘media_player.lg_smart_tv’, ‘on’) }}
      action: |
        {% if is_state(‘media_player.lg_smart_tv’, ‘on’) %}
          call-service
        {% else %}
          none
        {% endif %}
      service: webostv.button
      target:
        entity_id: media_player.lg_smart_tv 
  left:
    template: webostv
    tap_action:
      data:
        command: LEFT
  right:
    template: webostv
    tap_action:
      data:
        command: RIGHT
function mergeDeep(target: object, ...sources: [object]) {
    function isObject(item: object) {
        return (item && typeof item === 'object' && !Array.isArray(item));
    }

    if (!sources.length) {
        return target;
    }
    const source: object = sources.shift() as object;

    if (isObject(target) && isObject(source)) {
        for (const key in source) {
            if (isObject(source[key as keyof object])) {
                if (!target[key as keyof object]) Object.assign(target, { [key]: {} });
                mergeDeep(target[key as keyof object], source[key as keyof object]);
            } else {
                Object.assign(target, { [key]: source[key as keyof object] });
            }
        }
    }

    return mergeDeep(target, ...sources);
}

for (const key in customActions) {
    if (customActions[key].template) {
        const template = structuredClone(customActions[customActions[key].template])
        customActions[key] = mergeDeep(template, customActions[key])
    }
}

It may bit a little bit before I get to actually implementing this.

Javerre commented 1 month ago

@Nerwyn I think that looks like a perfect solution!

Javerre commented 1 month ago

Oops, finger trouble there!

Nerwyn commented 4 weeks ago

@Javerre can you try the latest 3.8.0 beta? I've added custom actions templates, along with a few other features and changes. It's working from what I see but you can probably test it more thoroughly.

Nerwyn commented 3 weeks ago

@Javerre if you have no feedback on the 3.8.0 beta I'll release it in a few days and then close this issue.

Nerwyn commented 3 weeks ago

Custom action templating added in 3.8.0.