About making inexpensive models smarter by providing tools and context. (local models, gpt-5-mini, gpt-4.1-mini, gpt-4o-mini ...)

Ok, my holidays are over and I’m back home.
So it’s time for a new LLM script. :stuck_out_tongue:

As music playback is the main driver for voice commands in our home, this was a non-brainer:
Group / ungroup music players:

Example usage:

  • Expand the playback in the living room to the kitchen
  • Add the bathroom to the playback in the living room
  • Ungroup all players from the living room
alias: Media Player Group Manager
icon: mdi:speaker-multiple
description: >+
  LLM Tool: Join/Unjoin media players, optionally clear existing members. 


  Hint: 

  Hint: 

  - If you are asked about joining / unjoing rooms, first look up the entity IDs
  of the media players with the 'Entity Index' tool and the tag 'MediaPlayer' to
  find the needed information. All of our media players are 'Inside' the house.

  - When asked to e.g. add the kitchen to the playback in the living room, then
  the living room has to be selected in the 'master' parameter in this case. The
  players that should be added need to be selected in the parameter 'members'.

  - The current grouping state of media_players can be checked using the 
  'Entity Index' tool ('Inside') with the tag 'MediaPlayer' and 'details' = true

mode: single
fields:
  operation:
    name: Operation
    required: true
    selector:
      select:
        options:
          - join
          - unjoin
          - clear_members
  master:
    name: Master
    description: Required for join and clear_members. The group leader / coordinator.
    required: false
    selector:
      entity:
        domain: media_player
  members:
    name: Members
    description: >-
      Players to join to the master (join) or to unjoin (unjoin). Multiple
      allowed.
    required: false
    selector:
      entity:
        domain: media_player
        multiple: true
  ungroup_first:
    name: Ungroup first
    description: >-
      If true (join only), unjoin the master and all provided members from any
      groups before joining.
    selector:
      boolean: {}
    default: false
  replace_existing:
    name: Replace existing members
    description: >-
      If true (join only), remove all current members from the master before
      joining the provided members.
    selector:
      boolean: {}
    default: false
sequence:
  - action: logbook.log
    data:
      name: "LLM MEDIA GROUP:"
      message: >-
        {{ this.entity_id }} — op={{ operation }} master={{ master }} members={{
        members|default([]) }} ungroup_first={{ ungroup_first|default(false) }}
        replace_existing={{ replace_existing|default(false) }}
      entity_id: "{{ this.entity_id }}"
  - variables:
      op: "{{ operation | lower }}"
      allowed_ops:
        - join
        - unjoin
        - clear_members
  - variables:
      normalized_members: |
        {% set ns = namespace(lst=[]) %} {% if members is defined and members %}
          {% if members is iterable and (members is not string) %}
            {% for m in members %}
              {% set ns.lst = ns.lst + [m] %}
            {% endfor %}
          {% else %}
            {% set ns.lst = ns.lst + [members] %}
          {% endif %}
        {% endif %} {{ ns.lst }}
  - variables:
      members_final: |
        {% set ns = namespace(lst=[]) %} {% for m in normalized_members %}
          {% if (master is not defined or m != master) and (m not in ns.lst) %}
            {% set ns.lst = ns.lst + [m] %}
          {% endif %}
        {% endfor %} {{ ns.lst }}
  - variables:
      errors: |
        {% set ns = namespace(lst=[]) %} {% if op not in allowed_ops %}
          {% set ns.lst = ns.lst + ['Invalid operation "' ~ op ~ '". Allowed: ' ~ (allowed_ops|join(', ')) ~ '.'] %}
        {% endif %}
        {% if op in ['join','clear_members'] and (not master) %}
          {% set ns.lst = ns.lst + ['"master" is required for operation ' ~ op ~ '.'] %}
        {% endif %}
        {% if op == 'join' and (members_final|count) == 0 %}
          {% set ns.lst = ns.lst + ['"members" must include at least one media_player for join.'] %}
        {% endif %}
        {% if op == 'clear_members' and (members_final|count) > 0 %}
          {% set ns.lst = ns.lst + ['Do not provide "members" for clear_members.'] %}
        {% endif %}
        {% if op == 'unjoin' and (members_final|count) == 0 and (not master) %}
          {% set ns.lst = ns.lst + ['Provide either "members" or a "master" to unjoin.'] %}
        {% endif %}
        {{ ns.lst }}
      has_errors: "{{ (errors | count) > 0 }}"
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ has_errors }}"
        sequence:
          - variables:
              result: |
                {{ {
                  'value': {
                    'status': 'error',
                    'messages': errors,
                    'received': {
                      'operation': op,
                      'master': master if master else None,
                      'members': members_final
                    }
                  }
                } }}
          - stop: ""
            response_variable: result
  - variables:
      current_group: |
        {% if master %}
          {{ state_attr(master, 'group_members') or [] }}
        {% else %}
          {{ [] }}
        {% endif %}
      current_non_master: |
        {% set ns = namespace(lst=[]) %} {% for m in current_group %}
          {% if not master or m != master %}
            {% set ns.lst = ns.lst + [m] %}
          {% endif %}
        {% endfor %} {{ ns.lst }}
      pre_unjoin: |
        {% if op == 'join' and (ungroup_first|default(false)) %}
          {% set ns = namespace(lst=[]) %}
          {% if master %}
            {% set ns.lst = ns.lst + [master] %}
          {% endif %}
          {% for m in members_final %}
            {% if m not in ns.lst %}
              {% set ns.lst = ns.lst + [m] %}
            {% endif %}
          {% endfor %}
          {{ ns.lst }}
        {% else %}
          {{ [] }}
        {% endif %}
      replace_unjoin: |
        {% if op == 'join' and (replace_existing|default(false)) %}
          {{ current_non_master }}
        {% else %}
          {{ [] }}
        {% endif %}
      ignored_flags: >
        {% set ns = namespace(lst=[]) %} {% if op != 'join' and
        (ungroup_first|default(false)) %}
          {% set ns.lst = ns.lst + ['ungroup_first'] %}
        {% endif %} {% if op != 'join' and (replace_existing|default(false)) %}
          {% set ns.lst = ns.lst + ['replace_existing'] %}
        {% endif %} {{ ns.lst }}
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ op == 'join' }}"
        sequence:
          - repeat:
              for_each: "{{ pre_unjoin }}"
              sequence:
                - action: media_player.unjoin
                  data:
                    entity_id: "{{ repeat.item }}"
          - repeat:
              for_each: "{{ replace_unjoin }}"
              sequence:
                - action: media_player.unjoin
                  data:
                    entity_id: "{{ repeat.item }}"
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ (members_final | count) > 0 }}"
                sequence:
                  - action: media_player.join
                    data:
                      entity_id: "{{ master }}"
                      group_members: "{{ members_final }}"
            default: []
          - variables:
              result: |
                {{ {
                  'value': {
                    'status': 'ok',
                    'operation': 'join',
                    'master': master,
                    'joined_members': members_final,
                    'ungroup_first': ungroup_first|default(false),
                    'replaced_existing': replace_existing|default(false),
                    'pre_unjoined': pre_unjoin,
                    'cleared_from_master': replace_unjoin,
                    'ignored_flags': ignored_flags
                  }
                } }}
          - stop: ""
            response_variable: result
      - conditions:
          - condition: template
            value_template: "{{ op == 'unjoin' }}"
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: "{{ (members_final | count) > 0 }}"
                sequence:
                  - repeat:
                      for_each: "{{ members_final }}"
                      sequence:
                        - action: media_player.unjoin
                          data:
                            entity_id: "{{ repeat.item }}"
                  - variables:
                      result: >-
                        {{ {'value':
                        {'status':'ok','operation':'unjoin','unjoined':
                        members_final, 'ignored_flags': ignored_flags}} }}
                  - stop: ""
                    response_variable: result
              - conditions:
                  - condition: template
                    value_template: "{{ master is not none }}"
                sequence:
                  - action: media_player.unjoin
                    data:
                      entity_id: "{{ master }}"
                  - variables:
                      result: >-
                        {{ {'value':
                        {'status':'ok','operation':'unjoin','unjoined':[master],
                        'ignored_flags': ignored_flags}} }}
                  - stop: ""
                    response_variable: result
            default:
              - variables:
                  result: >-
                    {{ {'value': {'status':'error','messages':['Provide either
                    "members" or a "master" to unjoin.']}} }}
              - stop: ""
                response_variable: result
      - conditions:
          - condition: template
            value_template: "{{ op == 'clear_members' }}"
        sequence:
          - repeat:
              for_each: "{{ current_non_master }}"
              sequence:
                - action: media_player.unjoin
                  data:
                    entity_id: "{{ repeat.item }}"
          - variables:
              result: |
                {{ {
                  'value': {
                    'status':'ok',
                    'operation':'clear_members',
                    'master': master,
                    'cleared_members': current_non_master
                  }
                } }}
          - stop: ""
            response_variable: result
    default:
      - variables:
          result: >-
            {{ {'value': {'status':'error','messages':['Invalid operation. Use
            join, unjoin or clear_members.']}} }}
      - stop: ""
        response_variable: result

I haven’t added anything to my prompt about this script so far as it seems to be detected and used fine.
But will most likely add a little sentence in the media section later and edit this script with it.
Maybe it won’t work well with smaller models otherwise.

edit:

Ok, I noticed a few times, that it didn’t react the right way to my requests.
I added a little bit more description to the script (updated the YAML code above) and added this to my prompt:

You can group media players with “Media Player Group Manager”. We use grouping a lot in the house. If we tell you e.g. that we want to listen to the same music in the kitchen as in the living room, this always means that you should group the kitchen media palyer (new member in this example) to the playback of the mediaplayer that is already playing in the living room (master in this example). Don’t simply start the same playback in the other room, always prefer grouping.

1 Like