Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Defined Global Custom Sources and Actions #157

Closed
skavan opened this issue Nov 6, 2024 · 26 comments
Closed

User Defined Global Custom Sources and Actions #157

skavan opened this issue Nov 6, 2024 · 26 comments
Labels
enhancement New feature or request

Comments

@skavan
Copy link

skavan commented Nov 6, 2024

Is your feature request related to a problem? Please describe.
I have multiple TV's from multiple brands (Google TV, Fire TV, Samsung, LG). Your card is excellent. I want to build one remote "style" for all my TV's. The challenge is that

  1. the yaml starts getting very long and unwieldy.
  2. The list of sources for several devices (especially Samsung) is incomplete, and it's difficult for you (and me) to keep up.
  3. I must duplicate all the custom sources in every remote card.

Describe the solution you'd like
I edited the Samsung section of universal-remote-card.min and added the missing entries...and it works beautifully. So I was thinking (for advanced users) of whether you could do the following:

  1. Have/support a json file in www/android-tv-card directory. Let's call it custom_sources_actions.json
  2. This file would allow users to build their own custom sources and actions in one place. An example structure is below.
  3. Your card would scan this file and load the name/id of the custom sections.
  4. In the gui or yaml, a user could specify the id/name of an entry (array or custom_sources/actions). i.e.
- type: custom:android-tv-card
  platform: Samsung TV
  media_player_id: media_player.samsung_q90_porch
  remote_id: remote.samsung_q90_porch
  user_sources: Samsung 2024 TV Sources
  user_actions: Samsung 2024 TV Actions
  user_icons: My Color Icons
  1. If specified, your code would merge the entries into the card, display them in the GUI and off we go!

Here's an example (missing the custom icons stuff) -- by the way the Samsung TV sources have all been tested and work so maybe you could update that! The approach below also handles custom icons for each source...

{
	"custom_sources": [
		{
			"name": "Samsung 2024 TV Sources",
			"id": "Samsung_2024_TV_src",
			"buttons": [
				{ "type": "button", "name": "dazn", "tap_action": { "action": "source", "source": "DAZN" }, "icon": "dazn" },
				{
					"type": "button",
					"name": "netflix",
					"tap_action": { "action": "source", "source": "Netflix" },
					"icon": "fapro:netflix#fullcolor"
				},
				{
					"type": "button",
					"name": "youtubetv",
					"tap_action": { "action": "source", "source": "YouTube TV" },
					"icon": "fapro:youtubetvicon#fullcolor"
				},
				{
					"type": "button",
					"name": "primevideo",
					"tap_action": { "action": "source", "source": "Prime Video" },
					"icon": "fapro:amazonprime#fullcolor"
				},
				{
					"type": "button",
					"name": "appletv",
					"tap_action": { "action": "source", "source": "Apple TV" },
					"icon": "appletv"
				},
				{
					"type": "button",
					"name": "peacock",
					"tap_action": { "action": "source", "source": "Peacock TV" },
					"icon": "fapro:peacock#fullcolor"
				},
				{
					"type": "button",
					"name": "youtube",
					"tap_action": { "action": "source", "source": "YouTube" },
					"icon": "fapro:youtube#fullcolor"
				},
				{
					"type": "button",
					"name": "paramount",
					"tap_action": { "action": "source", "source": "Paramount+" },
					"icon": "fapro:paramounto#fullcolor"
				},
				{
					"type": "button",
					"name": "hdmi",
					"tap_action": { "action": "source", "source": "HDMI" },
					"icon": "mdi:hdmi-port"
				},
				{
					"type": "button",
					"name": "max",
					"tap_action": { "action": "source", "source": "Max" },
					"icon": "max"
				},
				{
					"type": "button",
					"name": "hulu",
					"tap_action": { "action": "source", "source": "Hulu" },
					"icon": "fapro:hulu#fullcolor"
				},
				{
					"type": "button",
					"name": "disney",
					"tap_action": { "action": "source", "source": "Disney+" },
					"icon": "disney"
				}
			]
		}
	],
    "custom_actions": [
        {
            "name": "Samsung 2024 TV Actions",
            "id": "Samsung_2024_TV_act",
            "buttons": [
                {
                    "type": "button",
                    "name": "home",
                    "tap_action": { "action": "key", "key": "HOME" },
                    "icon": "mdi:home"
                }
            ]
        }
    ]
}

and if anyone is interested, here's my 'Universal Remote' (ignore last row, it's a WIP). This whole card is only 142 lines of yaml because I hacked the source .js -- which will, of course, get overwritten with your next release!

image

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

@skavan skavan added the enhancement New feature or request label Nov 6, 2024
@Nerwyn
Copy link
Owner

Nerwyn commented Nov 6, 2024

A global custom actions file wouldn't be a bad idea, but it would make sense to add the custom sources you've provided to the Samsung TV default source list. I should look into this further, as I'm concerned about how this would work with custom sources within the card itself.

@skavan
Copy link
Author

skavan commented Nov 6, 2024

Thanks for the consideration.
In answer to your question, priority order is (1 is higher):

  1. Custom Sources inside the card
  2. Sources loaded from global custom actions file
  3. Original sources from your code.
    We would merge on button name.

What I'm trying to avoid (I have 4 cards) is duplicating and maintaining those custom sources in every card!

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 8, 2024

Thinking more about this. It doesn't make sense to separate out custom actions and sources, they're just custom actions in code and config now. The file format can instead just be a JSON or YAML array (I can probably support both and parse on file extension).

[
    {
	"type": "button",
	"name": "netflix",
	"tap_action": { "action": "source", "source": "Netflix" },
	"icon": "fapro:netflix#fullcolor"
    },
    {
	"type": "button",
	"name": "youtubetv",
	"tap_action": { "action": "source", "source": "YouTube TV" },
	"icon": "fapro:youtubetvicon#fullcolor"
    },
    {
	"type": "button",
	"name": "primevideo",
	"tap_action": { "action": "source", "source": "Prime Video" },
	"icon": "fapro:amazonprime#fullcolor"
    },
]
- type: button
  name: netflix
  tap_action:
    action: source
    source: Netflix
  icon: fapro:netflix#fullcolor
- type: button
  name: youtubetv
  tap_action:
    action: source
    source: YouTube TV
  icon: fapro:youtubetv#fullcolor
- type: button
  name: primevideo
  tap_action:
    action: source
    source: Prime Video
  icon: fapro:amazonprime#fullcolor

Then different custom action lists can go in different files.

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 8, 2024

@skavan You can load custom actions from file in the latest alpha, and I've also added your Samsung custom sources to the default sources list. The file uses the format I described above. Set the field custom_actions_file in the config to point to the custom actions file on your Home Assistant server. I placed mine in the config www folder, and was able to point to it using the value local/remote_card_custom_actions.yaml

Config UI TBD

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 8, 2024

As of the latest alpha this has been completely integrated into the configuration UI.

@skavan
Copy link
Author

skavan commented Nov 8, 2024

Wow -- genius -- will test this out! Amazing...

s.

@skavan
Copy link
Author

skavan commented Nov 8, 2024

OMG - it just works!
So - I created a ur_firetv_core.json (see below, and u can use this to add these important sources to your core [see also play_pause actions at the end]). Some of these actions were very difficult to decipher, but I got there in the end.

then, in custom actions file: local/ur_firetv_core.json

then in layout:
image

results in:
image

Incredible work - you are amazingly talented.

A couple of other thoughts:

  1. in the UI, you show Default Keys and Sources. Would be awesome to have an additional section of "Custom". It might require switching to a tab view.

  2. Most Media Players have a source attribute. Would be good to expose this somewhere in the card. (i.e what am I watching?).

  3. Extending this 'system enhancement' would be an icon mapper json.
    with two keys: name: xxx icon: fapro:xxxxx#fullcolor. You might ask why is this necessary since we can already do this in the custom actions file. Well, two reasons:
    --- (i) I have noticed, that when I use a custom action, even if I don't specify a tap_action, the default tap_action is lost.
    --- (ii) I have several remotes (Fire TV, Samsung, Google, LG). They will all use their discrete "custom actions file" -- But they will all share a common icon set. For simpler users, who just want their own icons, this would make it easy.
    ---(iii) order is core, then custom_icons.json, then custom_actions.json, then the card itself.

----- p.s. for all the questions I see about SVG's...the easiest solution I have found is to use the hass-fontawesome HACS add-on. This allows one to drop custom svg's into the config/custom_icons directory and access them automagically.

  1. One other 'feature'. In my setup, I feed a firetv to a couple of TV's. So the media_player for power and volume is different than the firetv. It's easy to work around with a dedicated media player above your card. But, just a thought.

Here's where I'm at (and it's responsive too!):
image

[
    {
      "type": "button",
      "name": "youtubetv",
      "icon": "fapro:youtubetvicon#fullcolor",
      "tap_action": { "action": "source", "source": "com.amazon.firetv.youtube.tv" }
    },
    {
      "type": "button",
      "name": "primevideo",
      "icon": "fapro:amazonprime#fullcolor",
      "tap_action": {
        "action": "perform-action",
        "perform_action": "remote.send_command",
        "data": {
          "command": "sendevent /dev/input/event5 1 745 1 && sendevent /dev/input/event5 0 0 0 && sendevent /dev/input/event5 1 745 0 && sendevent /dev/input/event5 0 0 0"
        }
      }
    },
    {
      "type": "button",
      "name": "netflix",
      "icon": "fapro:netflix#fullcolor",
      "tap_action": { "action": "source", "source": "com.netflix.ninja" }
    },
    {
      "type": "button",
      "name": "youtube",
      "tap_action": { "action": "source", "source": "com.amazon.firetv.youtube" },
      "icon": "fapro:youtube#fullcolor",
      "styles": ":host { --size: 48px; }"
    },
    {
      "type": "button",
      "name": "peacock",
      "tap_action": { "action": "source", "source": "com.peacock.peacockfiretv" },
      "icon": "fapro:peacock#fullcolor",
      "styles": ":host { --size: 40px; }"
    },
    {
      "type": "button",
      "name": "paramount",
      "tap_action": { "action": "source", "source": "com.cbs.ott" },
      "icon": "fapro:paramounto#fullcolor",
      "styles": ":host { --size: 54px; }"
    },
    {
      "type": "button",
      "name": "appletv",
      "tap_action": { "action": "source", "source": "com.apple.tv" },
      "icon": "appletv",
      "styles": ":host { --size: 36px; }"
    },
    {
      "type": "button",
      "name": "max",
      "tap_action": {
        "action": "perform-action",
        "perform_action": "androidtv.adb_command",
        "target": {
          "entity_id": "{{ media_player_entity }}"
        },
        "data": {
          "command": "am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity"
        }
      },
      "icon": "fapro:max#fullcolor",
      "styles": ":host { --size: 40px; }"
    },
    {
      "type": "button",
      "name": "hulu",
      "tap_action": { "action": "source", "source": "Hulu" },
      "icon": "fapro:hulu2019#fullcolor"
    },
    {
      "type": "button",
      "name": "disney",
      "tap_action": { "action": "source", "source": "Disney+" },
      "icon": "disney",
      "styles": ":host { --icon-color: teal; }"
    },
    {
      "type": "button",
      "name": "play_pause",
      "icon": "mdi:play-pause",
      "tap_action": {
        "action": "perform-action",
        "perform_action": "remote.send_command",
        "data": {
          "command": "sendevent /dev/input/event5 4 4 786637 && sendevent /dev/input/event5 1 164 1 && sendevent /dev/input/event5 0 0 0 && sendevent /dev/input/event5 4 4 786637 && sendevent /dev/input/event5 1 164 0 && sendevent /dev/input/event5 0 0 0"
        }
      }
    }
  ]
  

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 8, 2024

Glad it's working for you!

  1. in the UI, you show Default Keys and Sources. Would be awesome to have an additional section of "Custom". It might require switching to a tab view.

It already does? If the custom actions do not appear for you for both imported from file and configured in remote custom actions there may be a bug. They appear consistently for me.

image

  1. Most Media Players have a source attribute. Would be good to expose this somewhere in the card. (i.e what am I watching?).

Use a label with a template on a custom action element.

label: '{{ state_attr("media_player.samsung", "source") }}'
  1. Extending this 'system enhancement' would be an icon mapper json.
    with two keys: name: xxx icon: fapro:xxxxx#fullcolor. You might ask why is this necessary since we can already do this in the custom actions file. Well, two reasons:
    --- (i) I have noticed, that when I use a custom action, even if I don't specify a tap_action, the default tap_action is lost.
    --- (ii) I have several remotes (Fire TV, Samsung, Google, LG). They will all use their discrete "custom actions file" -- But they will all share a common icon set. For simpler users, who just want their own icons, this would make it easy.
    ---(iii) order is core, then custom_icons.json, then custom_actions.json, then the card itself.

Probably out of scope of this issue. Making the icons customizable without creating new custom actions would make things much more complicated and I'm going to opt not to do that. I do want to add more custom icon support (like custom icon files) in the future, but probably not any time soon.

Default > custom action inheritance is now handled by the UI, which is why the tap action is lost in your custom actions file if not specified. I'd recommend creating all of your custom actions with the configuration UI, then copying them to your custom actions file. The file can also be a YAML which makes for easier copying.

  1. One other 'feature'. In my setup, I feed a firetv to a couple of TV's. So the media_player for power and volume is different than the firetv. It's easy to work around with a dedicated media player above your card. But, just a thought.

Why not create custom actions in the remote config for these actions? That's what I'm doing for volume and power since they're controlled by IR.

@skavan
Copy link
Author

skavan commented Nov 8, 2024

It already does? If the custom actions do not appear for you for both imported from file and configured in remote custom actions there may be a bug. They appear consistently for me.

Once I added a single custom action through the gui, it showed up!

A quick question for you. In the json files I have an action that requires entity to be set. Is there a way to plop a variable in there that will be evaluated (i.e. the current media_player_id or remote_id):

{
      "type": "button",
      "name": "max",
      "tap_action": {
        "action": "perform-action",
        "perform_action": "androidtv.adb_command",
        "target": {
          "entity_id": "{{ media_player_entity }}"
        },
        "data": {
          "command": "am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity"
        }
      },

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 8, 2024

Once I added a single custom action through the gui, it showed up!

I'll have to fix it not appearing when you only have file custom actions, that's a bug.

A quick question for you. In the json files I have an action that requires entity to be set. Is there a way to plop a variable in there that will be evaluated (i.e. the current media_player_id or remote_id):

You can access any fields in the current custom action using config.entity or similar fields in a template. When using a key, source, or perform-action action in a domain that uses one of those entities it should be available as {{ config.tap_action.target.entity_id }}. BUT it looks like I forgot to add adbcommand to the list here and will also need to fix that bug. Once that's fixed it should auto-populate the target entity ID for you.

FYI the Android TV ADB / Fire TV integration now provides a remote entity which I find performs better than the old adbcommand service.

@skavan
Copy link
Author

skavan commented Nov 8, 2024

A few hours later -- what a card! I just replace the entity id's and the custom_actions_file and boom. It works across my fire tv and samsung. Google TV next. THANK YOU.

my dashboard below - for others to benefit from.
175 lines -- and if we could get styles imported (exactly the same across 4 dashboards) we'd be at 123 lines!

I have two questions:

  1. I can't seem to style the touchpad. Maybe because its so nested? Am I missing a trick?
  2. Didn't understand how exactly to use your suggestion above {{ config.tap_action.target.entity_id }}
    in my custom_actions_file, I have:
    	"type": "button",
    	"name": "play_pause",
    	"icon": "mdi:play-pause",
    	"tap_action": {
    		"action": "perform-action",
    		"perform_action": "media_player.media_play_pause",
    		"target": { "entity_id": "media_player.samsung_q90_porch" },
    		"data": {}
    	}
    },```
    

How would I replace the target line using your approach?

Here's the full dashboard:

views:
  - title: Home
    type: panel
    cards:
      - type: custom:layout-card
        layout_type: custom:vertical-layout
        layout:
          max_width: 700
          card_margin: 0px
          margin: 12px
        cards:
          - type: custom:layout-card
            layout_type: custom:grid-layout
            layout:
              grid-template-columns: auto 1fr
              grid-template-rows: 50px
              place-items: center normal
              card_margin: 0
            layout_options:
              max_width: 700
            style:
              margin: 0px;
              align-items: center!important;
            cards:
              - type: custom:mushroom-chips-card
                card_mod:
                  style: |
                    ha-card {
                      justify-content: start;
                      background: red;
                      display: flex;
                      margin-bottom: 0px;
                    }
                chips:
                  - type: back
                    card_mod:
                      style: |
                        ha-card {
                          justify-content: center;
                          --chip-height: 36px;
                          width: 36px!important;
                          --chip-border-radius: 100%;
                          --chip-icon-size: 24px;
                        }
              - type: custom:bubble-card
                card_type: media-player
                entity: media_player.samsung_q90_porch
                hide:
                  play_pause_button: true
                  next_button: true
                  previous_button: true
                  volume_button: true
                show_state: false
                show_attribute: true
                attribute: source
                show_icon: false
                styles: |-
                  .bubble-name {
                    font-size: 18px;
                  }
                  .bubble-button-container {
                    gap: 0px;
                    margin-right: 4px;
                  } 
                  .bubble-sub-button-container {
                    gap: 0px;
                    column-gap: 4px;
                    margin-right: 0px;
                  } 
                  .bubble-sub-button {
                    background: rgba(var(--rgb-primary-text-color), 0.05);
                    border-radius: 12px;
                  }
                  .bubble-sub-button-icon {
                    --mdc-icon-size: 32px !important;
                  }
                  .bubble-name-container {
                    margin-left: 0px!important;
                  }
                  .bubble-media-player-container {
                    border-radius: 0px!important;
                    background: none;
                  }
                  .bubble-power-button {
                    --mdc-icon-size: 32px !important;
                    padding-top: 0px;
                  }
          - type: custom:android-tv-card
            rows:
              - - netflix
                - youtubetv
                - primevideo
                - youtube
                - peacock
                - paramount
                - appletv
                - max
                - hulu
                - disney
              - - - back
                  - settings
                  - home
                - touchpad
                - - volume_buttons
              - - null
                - custom1
                - custom2
                - custom3
                - null
              - - rewind
                - play_pause
                - fast_forward
            platform: Samsung TV
            custom_actions:
              - type: button
                name: now_playing
                icon: mdi:import
                entity_id: media_player.samsung_q90_porch
                value_attribute: source

            remote_id: remote.samsung_q90_series_65
            media_player_id: media_player.samsung_q90_porch
            custom_actions_file: local/ur_samsung_core.json
            styles: |
              #row-1 {
                flex-wrap: wrap;
                max-width: 720px;
                gap: 4px;
                row-gap: 8px;
                justify-content: center;
              } 

              #row-1 remote-button {
                border: 1px solid var(--disabled-color);
                min-width: 60px;
                --size: 38px;
                height: 48px;       
                flex-grow: 1;
                min-width: calc((100% - 16px) / 5); /* 4 gaps */
                max-width: calc((100% - 1rem) / 5); /* 4 gaps */
              }
              #row-2 {
                margin-top: 16px;
                height: 240px;
                align-items: stretch;
                --size: 32px;
              }
              #row-2 remote-button {
                --size: 32px;
                height: 48px;
                width: 48px;
                background: var(--secondary-background-color);
              }
              #row-2 #column-1 { 
                justify-content: space-between;
              }
              #row-2 #column-2 { 
                justify-content: space-between;
              }
              #row-3 {
                flex-wrap: wrap;
                max-width: 720px;
                margin: auto;
                margin-top: 12px;
                margin-bottom: 24px!important;
                gap: 4px;
                justify-content: space-inbetween;
              } 

              #row-3 remote-button {
                background: var(--secondary-background-color);
                height: 48px;
                width: 48px;
                --size: 32px;
              }

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 9, 2024

  1. I can't seem to style the touchpad. Maybe because its so nested? Am I missing a trick?

You need to create a custom action for the touchpad and use the appearance style field within that. The touchpad has an inner toucharea element you probably want to apply styles to. The inner toucharea-row divs are for containing the center and direction icons and labels.

image

  1. Didn't understand how exactly to use your suggestion above {{ config.tap_action.target.entity_id }}

As of the latest alpha, the custom action entity or remote media player ID (prioritized in that order for whichever is the correct domain for the service) will autofill in the target.entity_id field internally for the androidtv domain, you don't have to set it yourself. If you do want to reference it in something like a label, you can with that template. You should just leave it empty and it should just work.

{
  "type": "button",
  "name": "play_pause",
  "icon": "mdi:play-pause",
  "tap_action": {
    "action": "perform-action",
    "perform_action": "media_player.media_play_pause"
  }
}

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 9, 2024

4.2.0 is now in beta. @skavan if everything is working for you I'm going to release it tomorrow and close out this issue.

@skavan
Copy link
Author

skavan commented Nov 9, 2024

Thank you. All good here until my next crazy idea!

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 9, 2024

Released 4.2.0

@Nerwyn Nerwyn closed this as completed Nov 9, 2024
@skavan
Copy link
Author

skavan commented Nov 10, 2024

Couple of things. Well one for now. I'm seeing if the second is my issue...

  1. In a custom action, I can't override the entity. Even though I set it, it still uses the remote_id of the card, in this case remote.samsung_q90_series_65,
- type: button
  name: volume_up
  tap_action:
    action: key
    key: KEY_VOLUP
  hold_action:
    action: repeat
  icon: mdi:volume-plus
  entity_id: remote.fire_tv

@skavan
Copy link
Author

skavan commented Nov 10, 2024

EDIT: The below, may or may not be true, because the problem is deeper!
Revised thought:
In developer tools, this works:

action: androidtv.adb_command
target:
  entity_id: media_player.fire_tv
data:
  command: am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity

in custom_actions, this doesn't work (although I could swear it did with the alpha):

- name: max
  tap_action: 
    action: perform-action
    perform_action: androidtv.adb_command
    target: 
      entity_id: "media_player.firetv"
    data: 
      command: am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity

Original Thought:

OK - debugged a bit. My second "issue" is your auto-fill in of target entity works in a gui based custom action, but not when loaded from custom_actions_file:

GUI versions -- works:

    - type: button
      name: max
      tap_action:
        action: perform-action
        perform_action: androidtv.adb_command
        data:
          command: am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity
      icon: fapro:max#fullcolor
      styles: |-
        :host {
          --size: 40px;
        }

FILE version --- doesn't work:

{
      "type": "button",
      "name": "max",
      "tap_action": {
        "action": "perform-action",
        "perform_action": "androidtv.adb_command",
        "data": {
          "command": "am start -n com.hbo.hbonow/com.wbd.beam.BeamActivity"
        }
      },
      "icon": "fapro:max#fullcolor",
      "styles": ":host { --size: 40px; }"
    },

...and I tried avoiding this method (adbcommand) entirely, but your FireTV, "Max" button doesn't work for me at all.

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 14, 2024

  1. In a custom action, I can't override the entity. Even though I set it, it still uses the remote_id of the card, in this case remote.samsung_q90_series_65,

Malformed config, the remote ID should be in the tap_action and named differently.

- type: button
  name: volume_up
  tap_action:
    action: key
    key: KEY_VOLUP
    remote_id: remote.fire_tv
  hold_action:
    action: repeat
  icon: mdi:volume-plus  

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 14, 2024

OK - debugged a bit. My second "issue" is your auto-fill in of target entity works in a gui based custom action, but not when loaded from custom_actions_file:

I was initially able to recreate this issue, but after some more testing it seems to be a caching issue. Whenever you modify the custom actions file, it doesn't get updated in the card as it keeps using a cached version. I had to clear browser cache multiple times and change multiple things about the custom action in the file until it finally pulled down an updated copy of the file and worked correctly.

@skavan
Copy link
Author

skavan commented Nov 14, 2024

Thank you. This caching thing is a nightmare.

  1. I have the card fully operational for two devices, on the way to 5! I think I'm using it in a super advanced way...I want to share it...for others to copy from --- where do you suggest I do that. Here, in discussions, or in the homeassistant thread?

  2. I have one more enhancement request. It needs context. I have the card setup in yaml mode. I have a template sensor sensor.ui_remote, with an attribute called configs. It looks like this:

configs: |-
  {
    "porch_samsung": {
      "friendly_name": "Samsung + Tizen TV",
      "platform": "Samsung TV",
      "media_player_id": "media_player.samsung_q90_porch",
      "power_vol_id": "media_player.samsung_q90_porch",
      "remote_id": "remote.samsung_q90_porch",
      "keyboard_id": "media_player.samsung_q90_porch",
      "custom_actions_file": "local/universal_remote_samsung_tizen.yaml"
    },
    "porch_firetv": {
      "friendly_name": "Samsung + Fire TV",
      "platform": "Fire TV",
      "power_vol_id": "media_player.samsung_q90_porch",
      "media_player_id": "media_player.fire_tv",
      "remote_id": "remote.fire_tv",
      "keyboard_id": "media_player.fire_tv",
      "custom_actions_file": "local/ur_firetv_core.yaml"
    }
  }

Note, the keys:

  • porch_samsung: Samsung Q90 in the porch, controlling its own Tizen Media Player
  • porch_firetv : Samsung Q90 in the porch, controlling a FireTV input on HDMI3.

I then call my lovelace page, with a query string i.e. /my_view.yaml?mode=porch_samsung. I tightly couple android-tv-card with custom:config-template-card to make all the magic happen.

      - type: custom:config-template-card
        variables:
          # the magic. parse the query string to get the mode. use the mode to get the config. note the JSON.parse
          config: >
            var mode = new Proxy(new URLSearchParams(window.location.search), {get: (searchParams, prop) => searchParams.get(prop)})?.mode;
            var configs = JSON.parse(states['sensor.ui_remote'].attributes.configs);
            configs[mode && configs.hasOwnProperty(mode) ? mode : "porch_firetv"];
        entities:
          # any changes in these entities will trigger a refresh of the card.
          - sensor.ui_remote
          - "${vars['config'].media_player_id}"
          # note the power_vol_id, background and test_prop
        card:
          type: custom:android-tv-card
          platform: "${ config.platform }"
          remote_id: "${ config.remote_id }"
          media_player_id: "${ config.media_player_id }"
          custom_actions_file: "${ config.custom_actions_file }"
          power_vol_id: "${ config.power_vol_id }"
          selected_bg: var(--yellow-color);
          test_prop: 
            sub_prop: "I am a subprop!"
          card_mod:
            style: |
              ha-card {
                background: var(--ha-card-background, var(--card-background-color, #fff));
                border: 1px solid var(--ha-card-border-color, var(--divider-color, #e0e0e0));
                border-radius: var(--card-border-radius, 12px);
              }
          rows:
            - - netflix
              - youtubetv
              - primevideo
              - youtube
              - peacock
              - paramount
              - appletv
              - max
              - hulu
              - disney
            - - - back
                - settings
                - home
              - touchpad
              - - volume_buttons
            - - custom1
              - custom2
              - custom3
              - custom4
              - custom5
            - - rewind
              - play_pause
              - fast_forward  
          custom_actions:            
            # this is a placeholder for universal custom_actions. The majority come from custom_actions_file.
            - name: now_playing
              type: button
              icon: mdi:import
              entity_id: "${ config.media_player_id }"
              value_attribute: source
          styles: 
            !include /config/yaml/components/universal-remote/core-styles.yaml

Just change the query string and wow - whole new remote with exactly the same layout, but including custom remote specific buttons too.
In core-styles.yaml I can do some cool stuff like light up the background of a source button, if its the active source. What is super interesting is that I can see the additional properties I added to your card: power_vol_id, selected_bg and test_prop. This is great.
In fact crazy great!

  #row-1 remote-button[title={{ (state_attr(config.media_player_id, 'source')|lower|replace(' ','')|replace('+','')) or 'xxx' }} ] {
    background: {{ config.selected_bg }};
  }

Now, lets move to the custom_actions_file. In this example, I have plucked a button from my actions file for my fire_tv through my Q90 TV.
This button, sends a remote command to the Samsung to select HDMI3, and then navigates to the Samsung Tizen remote (note the url). The state setting change, forces a refresh of the remote card.
But that's not really the point. See below.

- name: custom4
  type: button
  icon: 'mdi:hdmi-port'
  source_match: 'JBL BAR 9.1'
  tap_action:
    action: fire-dom-event
    browser_mod:
      service: browser_mod.sequence
      data:
        sequence:
          - service: remote.send_command
            data:
              entity_id: remote.samsung_q90_porch
              command: ST_HDMI3
          - service: browser_mod.navigate
            data:
              path: /lovelace-yaml-1/0?mode=porch_samsung        
          - service: python_script.set_state
            data:
              entity_id: sensor.ui_remote
              state: >
                {{ range(1, 999) | random }}
  styles: |-
    :host {
      {% if is_state_attr('media_player.samsung_q90_porch', 'source', config.source_match) %}    
      background: var(--yellow-color);
      {% endif %}
    }

Inside the button, I have access to the Button config.
i.e.
{% if is_state_attr('media_player.samsung_q90_porch', 'source', config.source_match) %}
Works great. But what I don't have access to is the card config.

That's my enhancement request:

If I could get access to the card config variables, from inside a custom_action, I could eliminate so much hard-coding.
Even if not the whole config. A single variable/object that is passed down to the button would do.
Like:
{{ config.passthru_props.background }} or {{ parent.power_vol_id }}
Would be soooo cool. No need for UI support. This is an enhancement to your templating support that would rock.

Bonus:
Oh - while I have you. Do you think there is anyway to support an !include in the custom_actions_file. This bonus feature would allow me to move all 'fire_tv' common buttons into their own file. And add the firetv+samsung specific buttons from a small dedicated file. Right now, I will have a samsung+firetv.yaml and an lg+firetv.yaml that are 90% identical.

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 15, 2024

  1. I have the card fully operational for two devices, on the way to 5! I think I'm using it in a super advanced way...I want to share it...for others to copy from --- where do you suggest I do that. Here, in discussions, or in the homeassistant thread?

Discussions. It'll get lost here.

I have one more enhancement request. It needs context. I have the card setup in yaml mode. I have a template sensor sensor.ui_remote, with an attribute called configs. It looks like this:

That's really confusing and hard to follow. To boil it down, you want to access the entire card config in the templates of custom actions? That would be a lot of data to copy to each custom action. We should limit it to a subset. What about if this subset was copied to each custom action?

card:
  title: string
  remote_id: string
  media_player_id: string
  keyboard_id: string
  platform: Platform
  extra: {}

@skavan
Copy link
Author

skavan commented Nov 15, 2024

Perfect.
You could limit it to just extra if you wanted to have a no impact consequence to 99% of use cases. I'd personally, call it, pass_through rather than extra. But it's your card!

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 15, 2024

@skavan try the 4.2.1 alpha. You can access the parent card (minus array elements) using a template like {{ config.card.remote_id }}, no extra field required. If you add custom fields to that they should also be available in config.card.

@skavan
Copy link
Author

skavan commented Nov 15, 2024

Fabulous. Thank you. Will play with it on Sunday and let you know...

s.

@skavan
Copy link
Author

skavan commented Nov 17, 2024

Great...it's working well. Thank you.

s.

@Nerwyn
Copy link
Owner

Nerwyn commented Nov 17, 2024

FYI I removed the minus array elements restriction, so the entire card config is available in sub-elements templates.

Added in 4.2.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants