Universal Remote Card - Buttons, Touchpads, Sliders, and Keyboards for Multiple Platforms

No, please reread my previous comment. Use the general CSS styles field in the card configuration UI to change the value of --ha-dialog-surface-background (after updating to 4.10.0).

FYI patch version 4.10.3 renames --size to --icon-size to avoid a CSS custom property name conflict with the ha-adaptive-dialog height property. If you use this property in your custom remote styles, search and replace for all instances of --size with --icon-size.

An absolutely remarkable card.
I am controlling several different TV’s (One Samsung [Smart TV Mode], One Samsung, fed by a Fire TV streamer, and an LG, fed by a Fire TV streamer).

90% of the dashboard yaml is identical.
I just fill in the entities and point to a custom_actions_file that holds all the app commands etc…

So the Samsung + Fire TV is:

 - type: custom:universal-remote-card
        platform: Fire TV
        remote_id: remote.fire_tv_family_192_168_1_65
        media_player_id: media_player.fire_tv_family_192_168_1_65
        volume_entity: media_player.ma_soundbar_porch
        power_entity: media_player.sts_samsung_q90_porch
        mute_entity: media_player.sts_samsung_q90_porch
        room_name: Porch · Fire TV Remote
        custom_actions_file: local/ur_firetv.yaml

and the Samsung Smart TV version is:

- type: custom:universal-remote-card
        platform: Samsung TV
        remote_id: remote.sts_samsung_q90_porch
        media_player_id: media_player.sts_samsung_q90_porch
        volume_entity: media_player.ma_soundbar_porch
        power_entity: media_player.sts_samsung_q90_porch
        mute_entity: media_player.sts_samsung_q90_porch
        room_name: Porch · Samsung TV Remote
        custom_actions_file: local/ur_samsung.yaml

and the stylized remote is (100% URC):

Happy to share full dashboard yaml and the custom_actions_file if it helps anyone.

2 Likes

Nice! I like the scolling buttons at the top for app/input selection.

Hi! Love this integration, it works pretty well for my assortment of media devices.

I had a question though - is it possible to show the state of a button’s entity on the universal remote card? I’m trying to achieve something like this:


so I can show the current volume level on the remote itself (example image built with custom:button-cards where the middle one just shows entity state). Thanks!

You can create a custom remote element and use it’s label with a template to show the button entity state. You can use two other custom remote elements for the plus and minus buttons.

You may also be interested in custom card features, which has a spinbox made specifically for displaying and incrementing/decrementing numeric feature states/attributes.

1 Like

Thank you, the label change was pretty much exactly what I needed!

looks very nice, can you share your yaml and the custom actions

Version 4.11.0 adds keyboard support to the Apple TV platform, along with a few other small improvements. This release requires Home Assistant 2026.5.0, which adds keyboard actions to the Apple TV integration.

I'm using HAIR to learn IR commands from various remotes (mainly older non-IP commanded devices) and would like to integrate them into the Universal Remote card. HAIR creates buttons for each command instead of a remote device.

I tried to use generic remote but couldn't figure out how to map each predefined card button to a button entity. Is there an example or instructions on hwo to do this?

You have to create custom remote button elements in the elements tab of the configuration UI. After creating the element, set the name to be the same as a default one to inherit/overwrite it or create one with a new name and set the interaction tap action to press the button. See the custom elements section of the README for more information.

1 Like

Thanks. I missed that and now feel sily

1 Like

Is there a possibility to add nuvio icon and wuplayer icon please? Just updated and noticed streamio and a bunch of Australian icons got added.

Create a feature request with app and platform info and I'll get around to it eventually.

I love the Universal Remote Card! I have three TVs, use a streamline card holding the URC and some other entities (on/off for the speakers and volume control, art_mode, brightness, etc) and one button access to all of our (too many) subscriptions. I put those cards behind conditional cards, so that they don't clutter my dashboard when not in use. Fantastic! Thanks, Nerwyn!

2 Likes

Just wanted to drop in and express my appreciation for this awesome card. @Nerwyn, thank you for the effort in both creating it and for publishing such a comprehensive readme. There’s a mountain of knowledge in there if you’re willing to go look for it, and the examples seemed to be very thoughtfully assembled to cover the gamut in terms of adjustable parameters. I started with zero experience using CSS templates, so I was pretty happy to be able to create a universal remote for my theater that does a somewhat decent job of imitating the native appearance of the individual components and apps.

I do have a couple questions that I wasn’t able to run down, if anyone happens to know. Does this card accept the following template?

{{ states.media_player.apple_tv.attributes.media_duration is defined }}

I’d like to be able to suppress the slider for media position when it’s obviously not relevant, and the above seems to check ok in the template editor, but no luck using it in the layout section of the card. I’m also struggling to define a fixed height for a row that holds single icons or columns depending on input source. I’ve tried
#row-8 { height: 100px; } as an example in the general styles as well as .row {height…} in individual elements. I’m sure I’m missing something simple here.

2 Likes

I’m glad you like it so much! Your remotes look excellent.

This card (and custom card features) use an alternative frontend templating system I developed called ha-nunjucks, which has mostly the same syntax with some minor differences. One of them is the syntax of the states object, as I can’t use the same name for it and the states function. You just have to add an underscore before states.

{{ _states.media_player.apple_tv.attributes.media_duration is defined }}

BUT you should avoid using the states object directly in favor of the state_attr function, which is much more efficient.

{{ state_attr('media_player.apple_tv', 'media_duration') is defined }} 

To change an individual row height, unset its flex property so it doesn’t conform to its parent flexbox.

#row-8 {
  height: 100px;
  flex: unset;
}

Ah thanks, that did it!

Not sure if there are many other Apple TV users in here, but thought I’d share my result of recreating the physical remote. Not quite an exact replica since the remote uses a combination circlepad/touchpad, but still functional.

yaml:

type: custom:universal-remote-card
rows:
  - - power
  - - touchpad
  - - - menu
      - play_pause
      - volume_mute
    - - home
      - volume_up
      - volume_down
  - - null
    - null
  - - null
    - null
  - - null
    - null
styles: |-
  ha-card {
    background-color: #A2AAADE0;
    border-radius: 40px;
    width: 230px;
    box-shadow: 3px 4px 0px #A2AAAD90, 0px 0px 3px #111;
  }
  .empty-button {
    height: 92px;
    width: 84px;
  }
  #row-1 {
    justify-content: flex-end;
  }
  #row-2 {
      margin: 0px -6px -6px -6px;
  }
custom_actions:
  - name: power
    tap_action:
      action: key
      key: POWER
      platform: Apple TV
    hold_action:
      action: key
      key: suspend
    icon: mdi:power
    type: button
    styles: |
      :host {
        padding: 8px;
        --icon-size: 30px;
        --icon-color: #151515;
      }
      button {
        height: var(--button-size);
        width: var(--button-size);
        box-shadow:  0px -1px 0px #ccc, 0px 1px 1px #000;
        --button-size: 36px;
      }
  - type: touchpad
    name: touchpad
    tap_action:
      action: key
      key: select
    up:
      tap_action:
        action: key
        key: up
      hold_action:
        action: repeat
    down:
      tap_action:
        action: key
        key: down
      hold_action:
        action: repeat
    left:
      tap_action:
        action: key
        key: left
      hold_action:
        action: repeat
    right:
      tap_action:
        action: key
        key: right
      hold_action:
        action: repeat
    styles: |-

      toucharea {
        height: 212px;
        border-radius: 106px;
        background: radial-gradient(circle at top, #080808 15%, #323232 100%);
        --icon-color: #444;
      }
      .icon {
        bottom: 40px;
      }
  - name: menu
    tap_action:
      action: key
      key: menu
    icon: mdi:chevron-left
    type: button
    styles: |-
      :host {
        --icon-color: #E8E8E8;
      }
      button {
        background: radial-gradient(circle at top, #080808 15%, #323232 100%);
        border-radius: 50%;
        height: var(--button_size);
        width: var(--button_size);
        box-shadow: 0px -1px 0px #aaa, 2px 2px 0px #000;
        --button_size: 84px;
      }
  - name: play_pause
    tap_action:
      action: key
      key: select
    icon: mdi:play-pause
    type: button
    styles: |-
      .icon {
        color: #E8E8E8;
        transform: scaleX(0.7) scaleY(0.5);
      }
      button {
        background: radial-gradient(circle at top, #151515 75%, #202020 100%);
        border: 1px solid #444;
        height: var(--button_size);
        width: var(--button_size);
        box-shadow:  0px -1px 0px #aaa, 1px 1px 0px #000;
        --button_size: 84px;
      }
  - name: volume_mute
    tap_action:
      action: key
      key: MUTE
    icon: mdi:volume-variant-off
    type: button
    styles: |-
      :host {
        --icon-size: 32px;
      }
      .icon {
        color: #E8E8E8;
      }
      button {
        background: radial-gradient(circle at top, #151515 75%, #202020 100%);
        border: 1px solid #444;
        border-radius: 50%;
        height: var(--button_size);
        width: var(--button_size);
        box-shadow:  0px -1px 0px #aaa, 1px 1px 0px #000;
        --button_size: 84px;
      }
  - name: home
    tap_action:
      action: key
      key: home
    icon: mdi:television
    type: button
    styles: |-
      :host {
        padding-top: 1px;
        --icon-size: 34px;
        --icon-color: #E8E8E8;
      }
      button {
        background: radial-gradient(circle at top, #151515 75%, #202020 100%);
        height: var(--button_size);
        width: var(--button_size);
        border: 1px solid #444;
        border-radius: 42px;
        box-shadow: 0px -1px 0px #aaa, 1px 1px 0px #000;
        --button_size: 84px;
      }
  - name: volume_up
    tap_action:
      action: key
      key: VOLUME_UP
    hold_action:
      action: repeat
    icon: mdi:plus
    type: button
    styles: |
      :host {
        margin-bottom: -4px;
        --icon-size: 32px;
        --icon-color: #ddd;
      }
      .icon {
        margin-bottom: 4px;
      }
      button {
        background: radial-gradient(circle at top, #151515 75%, #151515 100%);
        border-radius: 42px 42px 0% 0%;
        box-shadow:  0px -1px 0px #aaa, 0px 0px 0px #000;
        height: 87px;
        width: 84px;
      }
  - name: volume_down
    tap_action:
      action: key
      key: VOLUME_DOWN
    hold_action:
      action: repeat
    icon: mdi:minus
    type: button
    styles: |-
      :host {
        margin-top: -4px;
        --icon-size: 32px;
        --icon-color: #ddd;
      }
      .icon {
        margin-top: 6px;

      }
      button {
        display: flex;
        flex-direction: column-reverse;
        background: radial-gradient(circle at top, #151515 75%, #303030 100%);
        border-radius: 0% 0% 42px 42px;
        box-shadow:  0px 0px 0px #000, 0px 1px 0px #000;
        height: 87px;
        width: 84px;
      }
platform: Apple TV

2 Likes

One of the things I’m surprised to see is that no one has posted anything for out of the box control of an Xbox.
One of the problems I first ran into is, I don’t know if its just me not know how to properly integrate it. But I had to manually override the config entry in the yaml code as my xbox would not show up on the list. Though my xbox account shows up there thought that doesn’t help for controlling the xbox.
Also had to build out the yaml code from scratch. I’ll include it in here perhaps someone with better knowledgeable them me can make it better.
Is it normal for there to be a huge delay from when I push the button the on the card and when the input registers in the Xbox.

type: custom:universal-remote-card
config_entry_id: 6b587a1241eff3eda0e840c563dbb524
remote_id: remote.dining_room_xbox_one
media_player_id: media_player.dining_room_jose
device: Xbox One
rows:
  - - back
    - power
    - home
    - menu
  - - circlepad
    - - volume_up
      - volume_down
      - volume_mute
  - - previous
    - rewind
    - play
    - pause
    - fast_forward
    - next
  - - plex
    - youtube
platform: Generic Remote
custom_actions:
  - type: circlepad
    name: circlepad
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: A
    up:
      icon: mdi:chevron-up
      tap_action:
        action: call-service
        service: remote.send_command
        target:
          entity_id: remote.dining_room_xbox_one
        data:
          command: Up
      hold_action:
        action: nothing
    down:
      icon: mdi:chevron-down
      tap_action:
        action: call-service
        service: remote.send_command
        target:
          entity_id: remote.dining_room_xbox_one
        data:
          command: Down
      hold_action:
        action: nothing
    left:
      icon: mdi:chevron-left
      tap_action:
        action: call-service
        service: remote.send_command
        target:
          entity_id: remote.dining_room_xbox_one
        data:
          command: Left
      hold_action:
        action: nothing
    right:
      icon: mdi:chevron-right
      tap_action:
        action: call-service
        service: remote.send_command
        target:
          entity_id: remote.dining_room_xbox_one
        data:
          command: Right
      hold_action:
        action: nothing
  - name: home
    icon: mdi:home
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: GoHome
      hold_action:
        action: nothing
    type: home
  - name: play
    icon: mdi:play
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Play
      hold_action:
        action: nothing
    type: play
  - name: pause
    icon: mdi:pause
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Pause
      hold_action:
        action: nothing
    type: pause
  - name: menu
    icon: mdi:microsoft-xbox
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Nexus
    type: menu
  - name: plex
    icon: mdi:plex
    tap_action:
      action: call-service
      service: media_player.play_media
      target:
        entity_id: media_player.dining_room_xbox_one
      data:
        media_content_id: 9NBLGGH3ZZVF
        media_content_type: app
    type: plex
  - name: youtube
    icon: mdi:youtube
    tap_action:
      action: call-service
      service: media_player.play_media
      target:
        entity_id: media_player.dining_room_xbox_one
      data:
        media_content_id: 9NDP7KTLK7W3
        media_content_type: app
    type: YouTube
  - name: volume_up
    icon: mdi:volume-plus
    tap_action:
      action: call-service
      service: media_player.volume_up
      target:
        entity_id: media_player.dining_room_xbox_one
    hold_action:
      action: repeat
    type: volume Up
  - name: volume_down
    icon: mdi:volume-minus
    tap_action:
      action: call-service
      service: media_player.volume_down
      target:
        entity_id: media_player.dining_room_xbox_one
    hold_action:
      action: repeat
    type: volume down
  - name: volume_mute
    icon: mdi:volume-off
    tap_action:
      action: call-service
      service: media_player.volume_mute
      target:
        entity_id: media_player.dining_room_xbox_one
    type: volume mute
  - name: back
    icon: mdi:arrow-left
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: GoBack
    type: back
  - type: rewind
    name: rewind
    icon: mdi:rewind
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Left
    hold_action:
      action: nothing
  - type: fast_forward
    name: fast_forward
    icon: mdi:fast-forward
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Right
    hold_action:
      action: nothing
  - type: Next Chapter
    name: next
    icon: mdi:skip-next
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Next
  - type: Previous Chapter
    name: previous
    icon: mdi:skip-previous
    tap_action:
      action: call-service
      service: remote.send_command
      target:
        entity_id: remote.dining_room_xbox_one
      data:
        command: Previous