Well, this one is for handling Input Selects.
Not that I was in need for that one, as Nathan already has one here for you.
But I created a custom GPT in ChatGPT yesterday and tried to feed its prompt with all the learnings from the past.
About at which topics or details it fails almost every single time because of HA specific behavior regarding Jinja syntax / features.
And indeed, it was the first time where I didn’t need to fix a lot bugs manually in the generated code.
Simply worked on the first try. ![]()
Ok, it’s more on the simple side of scripting. But still, it gave me a glimmer of hope. ![]()
alias: Input Select Tool
icon: mdi:form-select
mode: parallel
description: >
Assist LLM tool for - reading available options of input_select entities -
selecting one of those options.
Purpose:
- "get_options": Return the available options of an existing input_select.* entity
(including the current value).
- "select_option": Select one existing option for the given input_select.
IMPORTANT:
- You MUST provide the exact entity_id (e.g., "input_select.house_mode"). Do not invent entity names.
- To find entities, use "Entity Index" tool.
- Only works with the "input_select.*" domain.
Parameters:
- operation [required]: "get_options" | "select_option"
- entity_id [required]: exact entity_id
- desired_option [required for select_option]: String; must match one of the existing options.
Expected Output:
- Success (get_options):
result = {
entity_id: string,
current: string,
options: [string, ...]
}
error = null
- Success (select_option):
result = {
entity_id: string,
previous: string,
selected: string,
current: string,
success: boolean
}
error = null
- Error (any operation):
result = null
error = {
error: string,
(optional) allowed_operations: [...],
(optional) expected_domain: "input_select",
(optional) received: any,
(optional) allowed_options: [string, ...]
}
fields:
operation:
name: Operation
required: true
selector:
select:
options:
- get_options
- select_option
entity_id:
name: Entity ID (input_select.* only)
required: true
selector:
entity:
domain: input_select
desired_option:
name: Desired Option (for select_option)
required: false
selector:
text: null
sequence:
- action: logbook.log
data:
name: LLM INPUT SELECT TOOL (RAW)
message: >
operation={{ operation }}, entity_id={{ entity_id }}, desired_option={{
desired_option }}
entity_id: "{{ this.entity_id }}"
- choose:
- conditions:
- condition: template
value_template: "{{ operation in ['get_options','select_option'] }}"
sequence: []
default:
- variables:
error_result: |
{{ {
'error': 'unsupported operation',
'allowed_operations': ['get_options','select_option'],
'received': operation
} }}
- stop: Unsupported operation
response_variable: error_result
- variables:
eid: "{{ entity_id | string }}"
entity_exists: "{{ states(eid) is not none and states(eid) != 'unknown' }}"
is_input_select: "{{ eid | regex_search('^input_select\\..+') }}"
- choose:
- conditions:
- condition: template
value_template: "{{ entity_exists and is_input_select }}"
sequence: []
default:
- variables:
error_result: |
{{ {
'error': 'Invalid or unknown entity_id. Provide an existing input_select entity.',
'expected_domain': 'input_select',
'received': eid
} }}
- stop: Invalid entity
response_variable: error_result
- variables:
options_list_raw: "{{ state_attr(eid, 'options') }}"
options_list: >
{%- set ns = namespace(list=[]) -%} {%- if options_list_raw is iterable
and not (options_list_raw is string) -%}
{%- for o in options_list_raw -%}
{%- if o is string -%}
{%- set ns.list = ns.list + [ o ] -%}
{%- else -%}
{%- set ns.list = ns.list + [ (o|string) ] -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%} {{ ns.list }}
- choose:
- conditions:
- condition: template
value_template: "{{ options_list | length > 0 }}"
sequence: []
default:
- variables:
error_result: |
{{ {
'error': 'Entity has no options attribute or it is empty.',
'received': { 'entity_id': eid }
} }}
- stop: No options available
response_variable: error_result
- choose:
- conditions:
- condition: template
value_template: "{{ operation == 'get_options' }}"
sequence:
- variables:
result: |
{{ {
'entity_id': eid,
'current': states(eid),
'options': options_list,
'count': (options_list | length)
} }}
- stop: ""
response_variable: result
- choose:
- conditions:
- condition: template
value_template: "{{ operation == 'select_option' }}"
sequence:
- choose:
- conditions:
- condition: template
value_template: >
{{ desired_option is defined and (desired_option | string
| trim) != '' }}
sequence: []
default:
- variables:
error_result: |
{{ {
'error': 'Missing parameter: desired_option (string) for select_option.'
} }}
- stop: Missing desired_option
response_variable: error_result
- variables:
want: "{{ desired_option | string }}"
ci: "{{ true }}"
matches: >
{%- set ns = namespace(list=[]) -%} {%- for opt in options_list
-%}
{%- if ci -%}
{%- if (opt | lower) == (want | lower) -%}
{%- set ns.list = ns.list + [ opt ] -%}
{%- endif -%}
{%- else -%}
{%- if opt == want -%}
{%- set ns.list = ns.list + [ opt ] -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%} {{ ns.list }}
resolved_option: |
{%- if (matches | length) == 1 -%}
{{ matches[0] }}
{%- else -%}
{{ none }}
{%- endif -%}
- choose:
- conditions:
- condition: template
value_template: "{{ operation == 'select_option' and (resolved_option is string) }}"
sequence: []
default:
- variables:
error_result: |
{{ {
'error': 'desired_option not found or ambiguous for this entity.',
'allowed_options': options_list
} }}
- stop: Invalid desired_option
response_variable: error_result
- variables:
prev: "{{ states(eid) }}"
- action: input_select.select_option
target:
entity_id: "{{ eid }}"
data:
option: "{{ resolved_option }}"
- variables:
curr: "{{ states(eid) }}"
result: |
{{ {
'entity_id': eid,
'previous': prev,
'selected': resolved_option,
'current': curr,
'success': (curr == resolved_option)
} }}
- stop: ""
response_variable: result
I added this to the LLMs prompt:
Switching modes or selections from input_select entities:
If asked about changing modes, setting things to specific values or start something that sounds like one out of multiple choices, this often is realized by a input_select in the system.
You can list them with the ‘Entity Index’ tool and the tag ‘Selections’.
To get the possible values to select use the ‘get_options’ action of the ‘Input Select Tool’ tool.
If I tell you that something is available as input_select, use this knowledge.
I think it’s a quite nice pattern.
One example: I created an input select with no-selection, charge, complete-cleaning, <area1>, <area2>, drive-to-maintenance-position, …
In one part of my prompt I give the LLM application specific context that isn’t self-explaining.
I simply added there, that it can control the vacuum cleaner with an input select and that the action starts immediately once it selects an option.
Worked perfectly fine.
I bet there will be quite some use cases where this saves you from writing specific LLM scripts for a device / application.