Simplifying (and extending) code to run FLIRC IR receiver integration with WiiM devices

Hi, for a while I’ve been running an integration based around a FLIRC IR receiver which allows me to use my Harmony hub to control a WiiM streamer - indeed, that’s my main if not sole use of Home Assistant. I picked up how to do this from various sources on the WiiM forum and from sites like Controlling Home Assistant with an Old Remote – The Helpful Idiot as well as getting snippets of code and hints in this forum. I have programmed with Fortran, COBOL etc a good few decades ago and of late dabbled a bit with Excel macros and code scavenged on the 'net, but admit to finding YAML opaque and frustrating.

My automations.yaml and configuration.yaml files as are below.

# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes

automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

recorder:
  purge_keep_days: 5
  commit_interval: 30

keyboard_remote:
- device_name: 'flirc.tv flirc Keyboard'
  type: 'key_down'
- device_descriptor: /dev/input/event0
  type: 'key_down'
  
shell_command:
  wiim_ultra_pause_toggle_25: curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:onepause
  wiim_ultra_play_preset1_2:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:1
  wiim_ultra_play_preset2_3:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:2
  wiim_ultra_play_preset3_4:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:3
  wiim_ultra_play_preset4_5:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:4
  wiim_ultra_play_preset5_6:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:5
  wiim_ultra_play_preset6_7:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:6
  wiim_ultra_play_preset7_8:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:7
  wiim_ultra_play_preset8_9:  curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:8
  wiim_ultra_play_preset9_10: curl -k https://192.168.1.106/httpapi.asp?command=MCUKeyShortClick:9
  wiim_ultra_previous_48:     curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:prev
  wiim_ultra_next_33:         curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:next
  wiim_ultra_stop:            curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:stop
  wiim_ultra_volume:          curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:vol:50
  wiim_ultra_mute_66:         curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:mute:1
  wiim_ultra_unmute:          curl -k https://192.168.1.106/httpapi.asp?command=setPlayerCmd:mute:0
  wiim_ultra_shutdown_28:     curl -k https://192.168.1.106/httpapi.asp?command=setShutdown:0
  
 
##

rest_command:
  lms_ultra_play_clyde2:
    url: http://192.168.1.154:9000/Default/plugins/Favorites/index.html?sess=1&index=0&player=c0%3Af5%3A35%3Adc%3Aee%3A5f&action=play
    method: GET
#### automations.yaml
id: '1722958316639'
alias: WiiM Ultra flIRc
description: Run WiiM API calls from remote
trigger:
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 25
    id: "25"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 2
    id: "2"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 3
    id: "3"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 4
    id: "4"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 5
    id: "5"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 6
    id: "6"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 7
    id: "7"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 8
    id: "8"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 9
    id: "9"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 10
    id: "10"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 48
    id: "48"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 23
    id: "23"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 33
    id: "33"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 66
    id: "66"
  - platform: event
    event_type: keyboard_remote_command_received
    event_data:
      key_code: 28
    id: "28"
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - "25"
        sequence:
          - service: shell_command.wiim_ultra_pause_toggle_25
            metadata: {}
            data: {}
      - conditions:
          - condition: trigger
            id:
              - "2"
        sequence:
          - service: shell_command.wiim_ultra_play_preset1_2
      - conditions:
          - condition: trigger
            id:
              - "3"
        sequence:
          - service: shell_command.wiim_ultra_play_preset2_3
      - conditions:
          - condition: trigger
            id:
              - "4"
        sequence:
          - service: shell_command.wiim_ultra_play_preset3_4
      - conditions:
          - condition: trigger
            id:
              - "5"
        sequence:
          - service: shell_command.wiim_ultra_play_preset4_5
      - conditions:
          - condition: trigger
            id:
              - "6"
        sequence:
          - service: shell_command.wiim_ultra_play_preset5_6
      - conditions:
          - condition: trigger
            id:
              - "7"
        sequence:
          - service: shell_command.wiim_ultra_play_preset6_7
      - conditions:
          - condition: trigger
            id:
              - "8"
        sequence:
          - service: shell_command.wiim_ultra_play_preset7_8
      - conditions:
          - condition: trigger
            id:
              - "9"
        sequence:
          - service: shell_command.wiim_ultra_play_preset8_9
      - conditions:
          - condition: trigger
            id:
              - "10"
        sequence:
          - service: shell_command.wiim_ultra_play_preset9_10
      - conditions:
          - condition: trigger
            id:
              - "48"
        sequence:
          - service: shell_command.wiim_ultra_previous_48
      - conditions:
          - condition: trigger
            id:
              - "23"
        sequence:
          - service: rest_command.lms_ultra_play_clyde2
            response_variable: clyde2
      - conditions:
          - condition: trigger
            id:
              - "33"
        sequence:
          - service: shell_command.wiim_ultra_next_33
      - conditions:
          - condition: trigger
            id:
              - "66"
        sequence:
          - service: shell_command.iim_ultra_mute_66
      - conditions:
          - condition: trigger
            id:
              - "28"
        sequence:
          - service: shell_command.wiim_ultra_shutdown_28
mode: single

I’ve a few issues I’d like to resolve:

I’d like to simplify the YAML as the repetitive triggers and actions are calling out for a more compact sequence, if possible. For example, in the trigger section, can I have one trigger that sets “Id” to the keycode detected rather than the repeated sequence I have of “if keycode is x, set id to x elseif keycode is y set id to y elseif etc etc”?

Similarly in the action part, can I have a compact action that runs the corresponding shell command as a variable e.g. run the shell command "service: ‘shell_command.wiim_ultra_pause_toggle_’ + Id " or similar, if you follow me. I appreciate I’d need to simplify the shell command list in the configuration.yaml to rename them appropriately as shell_command.wiim_keycode_x, y etc. This would just be for a specified set of keycodes.

Also, given that not all the keycodes I might use will have a corresponding WiiM API call (like volume up/down, or easily ascertaining the current volume), I may need to use media_player service requests for them, for example this snippet:

action: media_player.volume_set
metadata: {}
data:
  volume_level: >-
    {{ state_attr('media_player.wiim_ultra_ee5f_2', 'volume_level') | float +
    0.02 }}
target:
  entity_id: media_player.wiim_ultra_ee5f_2

I’d also want to extend this to allow me to change which player I want the commands to apply to, so I’d need the API call IP address and the media_player device name to be variable.

If I were to express this in non YAML pseudo code (please forgive the jumble), it’d be something along these lines:

IP_addr = '192.168.1.106'
Device_name = 'wiim_ultra_ee5f_2'
Array API_keycodes = {25, 2, 3, 48, 28,...}
Array API_calls = {'setPlayerCmd:onepause', 'MCUKeyShortClick:1', 'MCUKeyShortClick:2', 'setPlayerCmd:prev', 'setShutdown:0', ...}

If keypress detected then
  set Id to keypress
  If Id is in API_keycodes then run shell command " 'curl -k https://' + IP_addr + '/httpapi.asp?command=' + API_calls(Match(Id,API_keycodes))"
    Elseif Id = 67 then run media_player.volume_set for corresponding Device_name # for when there isn't a corresponding API call
    Elseif Id = 92 then IP_Addr = '192.168.1.108' ; Device_name = 'wiim_amp_ultra' # swap device
    Elseif Id = 93 then IP_Addr = '192.168.1.106' ; Device_name = 'wiim_ultra_ee5f_2' #swap device
  Endif
Endif

Is this feasible in Home Assistant/ YAML?

Thanks

Much as I avoid ChatGPT and similar AI sites for general search, I must say I’ve been very impressed with it providing me a solution for this query after a sequence of back and forward requests, with me asking it to simplify my existing Yaml files, as well as asking it to add other bits either I or it thought of as we progressed.

The final result (so far…) is a package that not only does what I had before but also allows me to switch between WiiM devices from my harmony hub remote and Flirc dongle.

Here’s the abridged configuration.yaml


# Loads default set of integrations. Do not remove.
default_config:

# Load frontend themes from the themes folder
frontend:
  themes: !include_dir_merge_named themes

homeassistant:
  packages: !include_dir_named packages
  
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml

recorder:
  purge_keep_days: 5
  commit_interval: 30

keyboard_remote:
- device_name: 'flirc.tv flirc Keyboard'
  type: 'key_down'
- device_descriptor: /dev/input/event0
  type: 'key_down'

And the packaged Wiim_flirc.yaml file I saved to the config/packages folder:


# ----------------------------
# 🎛 Input Helpers
# ----------------------------
input_text:
  wiim_ip_address:
    name: WiiM IP Address
    initial: 192.168.1.106

  wiim_device_name:
    name: WiiM Ultra
    initial: media_player.wiim_ultra_ee5f_2

# ----------------------------
# 🔧 Shell & REST Commands
# ----------------------------
shell_command:
  wiim_command: 'curl -k "https://{{ states("input_text.wiim_ip_address") }}/httpapi.asp?command={{ command }}"'

rest_command:
  lms_ultra_play_clyde2:
    url: "http://192.168.1.154:9000/Default/plugins/Favorites/index.html?sess=1&index=0&player=c0%3Af5%3A35%3Adc%3Aee%3A5f&action=play"
    method: GET

# ----------------------------
# 🤖 Automation
# ----------------------------
automation:
  - alias: FLIRC - WiiM Unified Control
    trigger:
      - platform: event
        event_type: keyboard_remote_command_received
        event_data:
          device_name: 'flirc.tv flirc Keyboard'

    variables:
      key_map:
        # ---- WiiM HTTP API commands ----
        25: setPlayerCmd:onepause
        2: MCUKeyShortClick:1
        3: MCUKeyShortClick:2
        4: MCUKeyShortClick:3
        5: MCUKeyShortClick:4
        6: MCUKeyShortClick:5
        7: MCUKeyShortClick:6
        8: MCUKeyShortClick:7
        9: MCUKeyShortClick:8
        10: MCUKeyShortClick:9
        33: setPlayerCmd:next
        48: setPlayerCmd:prev
        66: setPlayerCmd:mute:1
        28: setShutdown:0

        # ---- Volume via media_player ----
        171: vol_up
        172: vol_down

        # ---- Source selection ----
        180:
          type: media_player_service
          service: media_player.select_source
          data:
            source: "Optical"
        181:
          type: media_player_service
          service: media_player.select_source
          data:
            source: "Bluetooth"
        182:
          type: media_player_service
          service: media_player.select_source
          data:
            source: "Line In"
        183:
          type: media_player_service
          service: media_player.select_source
          data:
            source: "Wi-Fi"

        # ---- WiiM Profile Switches ----
        24:
          type: set_device_profile
          device: media_player.wiim_ultra_ee5f_2
          ip: "192.168.1.106"
        34:
          type: set_device_profile
          device: media_player.wiim_amp_ultra_9794_3
          ip: "192.168.1.168"
#        212:
#          type: set_device_profile
#          device: media_player.wiim_pro_kitchen
#          ip: "10.0.0.53"

        # ---- LMS REST call ----
        23:
          type: rest_command
          command: rest_command.lms_ultra_play_clyde2

    action:
      - variables:
          key_code: "{{ trigger.event.data.key_code }}"
          long_press: "{{ trigger.event.data.long_press | default(false) }}"
          action_data: "{{ key_map[key_code] if key_code in key_map else none }}"

      - choose:
          # 🧠 REST Command
          - conditions: "{{ action_data is mapping and action_data.type == 'rest_command' }}"
            sequence:
              - service: "{{ action_data.command }}"

          # 🎯 WiiM API via shell_command
          - conditions: "{{ action_data is string }}"
            sequence:
              - service: shell_command.wiim_command
                data:
                  command: "{{ action_data }}"

          # 🎵 Source selection
          - conditions: "{{ action_data.type == 'media_player_service' and action_data.service == 'media_player.select_source' }}"
            sequence:
              - service: media_player.select_source
                target:
                  entity_id: "{{ states('input_text.wiim_device_name') }}"
                data:
                  source: "{{ action_data.data.source }}"
              - service: persistent_notification.create
                data:
                  title: "Input Source Changed"
                  message: "Selected source: {{ action_data.data.source }}"

          # 🔊 Volume Up
          - conditions: "{{ action_data == 'vol_up' }}"
            sequence:
              - service: media_player.volume_set
                target:
                  entity_id: "{{ states('input_text.wiim_device_name') }}"
                data:
                  volume_level: >-
                    {{
                      [
                        (state_attr(states('input_text.wiim_device_name'), 'volume_level') | float + 0.02),
                        1.0
                      ] | min | round(2)
                    }}

          # 🔉 Volume Down
          - conditions: "{{ action_data == 'vol_down' }}"
            sequence:
              - service: media_player.volume_set
                target:
                  entity_id: "{{ states('input_text.wiim_device_name') }}"
                data:
                  volume_level: >-
                    {{
                      [
                        (state_attr(states('input_text.wiim_device_name'), 'volume_level') | float - 0.02),
                        0
                      ] | max | round(2)
                    }}

          # 🔁 Device profile switch
          - conditions: "{{ action_data.type == 'set_device_profile' }}"
            sequence:
              - service: input_text.set_value
                target:
                  entity_id: input_text.wiim_device_name
                data:
                  value: "{{ action_data.device }}"
              - service: input_text.set_value
                target:
                  entity_id: input_text.wiim_ip_address
                data:
                  value: "{{ action_data.ip }}"
              - service: persistent_notification.create
                data:
                  title: "WiiM Device Switched"
                  message: "Now controlling {{ action_data.device }} at {{ action_data.ip }}"

    mode: single

I’ll still play around with it a little and add/revise the placeholder key codes but it’s now pretty close to all I’d want it to do.