diff --git a/src/components/ha-conversation-agent-picker.ts b/src/components/ha-conversation-agent-picker.ts
new file mode 100644
index 0000000000..5175e99f35
--- /dev/null
+++ b/src/components/ha-conversation-agent-picker.ts
@@ -0,0 +1,109 @@
+import {
+ css,
+ CSSResultGroup,
+ html,
+ LitElement,
+ nothing,
+ PropertyValueMap,
+} from "lit";
+import { customElement, property, state } from "lit/decorators";
+import { fireEvent } from "../common/dom/fire_event";
+import { stopPropagation } from "../common/dom/stop_propagation";
+import { Agent, listAgents } from "../data/conversation";
+import { HomeAssistant } from "../types";
+import "./ha-list-item";
+import "./ha-select";
+import type { HaSelect } from "./ha-select";
+
+const DEFAULT = "default_agent_option";
+@customElement("ha-conversation-agent-picker")
+export class HaConversationAgentPicker extends LitElement {
+ @property() public value?: string;
+
+ @property() public label?: string;
+
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ type: Boolean, reflect: true }) public disabled = false;
+
+ @property({ type: Boolean }) public required = false;
+
+ @state() _agents?: Agent[];
+
+ @state() _defaultAgent: string | null = null;
+
+ protected render() {
+ if (!this._agents) {
+ return nothing;
+ }
+ const value = this.value ?? DEFAULT;
+ return html`
+
+
+ ${this.hass!.localize(
+ "ui.components.coversation-agent-picker.default",
+ {
+ default: this._agents.find(
+ (agent) => agent.id === this._defaultAgent
+ )?.name,
+ }
+ )}
+
+ ${this._agents.map(
+ (agent) =>
+ html`${agent.name}`
+ )}
+
+ `;
+ }
+
+ protected firstUpdated(
+ changedProperties: PropertyValueMap | Map
+ ): void {
+ super.firstUpdated(changedProperties);
+ listAgents(this.hass).then((agents) => {
+ this._agents = agents.agents;
+ this._defaultAgent = agents.default_agent;
+ });
+ }
+
+ static get styles(): CSSResultGroup {
+ return css`
+ ha-select {
+ width: 100%;
+ }
+ `;
+ }
+
+ private _changed(ev): void {
+ const target = ev.target as HaSelect;
+ if (
+ !this.hass ||
+ target.value === "" ||
+ target.value === this.value ||
+ (this.value === undefined && target.value === DEFAULT)
+ ) {
+ return;
+ }
+ this.value = target.value === DEFAULT ? undefined : target.value;
+ fireEvent(this, "value-changed", { value: this.value });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-conversation-agent-picker": HaConversationAgentPicker;
+ }
+}
diff --git a/src/components/ha-selector/ha-selector-conversation-agent.ts b/src/components/ha-selector/ha-selector-conversation-agent.ts
new file mode 100644
index 0000000000..21dcfe12d7
--- /dev/null
+++ b/src/components/ha-selector/ha-selector-conversation-agent.ts
@@ -0,0 +1,45 @@
+import { css, html, LitElement } from "lit";
+import { customElement, property } from "lit/decorators";
+import { ConversationAgentSelector } from "../../data/selector";
+import { HomeAssistant } from "../../types";
+import "../ha-conversation-agent-picker";
+
+@customElement("ha-selector-conversation_agent")
+export class HaConversationAgentSelector extends LitElement {
+ @property() public hass!: HomeAssistant;
+
+ @property() public selector!: ConversationAgentSelector;
+
+ @property() public value?: any;
+
+ @property() public label?: string;
+
+ @property() public helper?: string;
+
+ @property({ type: Boolean }) public disabled = false;
+
+ @property({ type: Boolean }) public required = true;
+
+ protected render() {
+ return html``;
+ }
+
+ static styles = css`
+ ha-conversation-agent-picker {
+ width: 100%;
+ }
+ `;
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-selector-conversation_agent": HaConversationAgentSelector;
+ }
+}
diff --git a/src/components/ha-selector/ha-selector.ts b/src/components/ha-selector/ha-selector.ts
index f387867025..7d4e456e60 100644
--- a/src/components/ha-selector/ha-selector.ts
+++ b/src/components/ha-selector/ha-selector.ts
@@ -17,6 +17,7 @@ const LOAD_ELEMENTS = {
boolean: () => import("./ha-selector-boolean"),
color_rgb: () => import("./ha-selector-color-rgb"),
config_entry: () => import("./ha-selector-config-entry"),
+ conversation_agent: () => import("./ha-selector-conversation-agent"),
constant: () => import("./ha-selector-constant"),
date: () => import("./ha-selector-date"),
datetime: () => import("./ha-selector-datetime"),
diff --git a/src/components/ha-theme-picker.ts b/src/components/ha-theme-picker.ts
index 95c568f592..47aadd7e62 100644
--- a/src/components/ha-theme-picker.ts
+++ b/src/components/ha-theme-picker.ts
@@ -1,4 +1,3 @@
-import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
diff --git a/src/data/conversation.ts b/src/data/conversation.ts
index 3745271c8b..d74c64d0f6 100644
--- a/src/data/conversation.ts
+++ b/src/data/conversation.ts
@@ -56,6 +56,11 @@ export interface AgentInfo {
attribution?: { name: string; url: string };
}
+export interface Agent {
+ id: string;
+ name: string;
+}
+
export const processConversationInput = (
hass: HomeAssistant,
text: string,
@@ -70,9 +75,20 @@ export const processConversationInput = (
language,
});
-export const getAgentInfo = (hass: HomeAssistant): Promise =>
+export const listAgents = (
+ hass: HomeAssistant
+): Promise<{ agents: Agent[]; default_agent: string | null }> =>
+ hass.callWS({
+ type: "conversation/agent/list",
+ });
+
+export const getAgentInfo = (
+ hass: HomeAssistant,
+ agent_id?: string
+): Promise =>
hass.callWS({
type: "conversation/agent/info",
+ agent_id,
});
export const prepareConversation = (
diff --git a/src/data/selector.ts b/src/data/selector.ts
index 3ee0d020b7..1282323595 100644
--- a/src/data/selector.ts
+++ b/src/data/selector.ts
@@ -14,6 +14,7 @@ export type Selector =
| BooleanSelector
| ColorRGBSelector
| ColorTempSelector
+ | ConversationAgentSelector
| ConfigEntrySelector
| ConstantSelector
| DateSelector
@@ -85,6 +86,11 @@ export interface ColorTempSelector {
} | null;
}
+export interface ConversationAgentSelector {
+ // eslint-disable-next-line @typescript-eslint/ban-types
+ conversation_agent: {} | null;
+}
+
export interface ConfigEntrySelector {
config_entry: {
integration?: string;
diff --git a/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts b/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts
index 64b4b9efb5..15861616e2 100644
--- a/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts
+++ b/src/panels/config/voice-assistants/dialog-voice-assistant-pipeline-detail.ts
@@ -125,7 +125,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
name: "conversation_engine",
required: true,
selector: {
- text: {},
+ conversation_agent: {},
},
},
{
diff --git a/src/translations/en.json b/src/translations/en.json
index 6ee28472e1..7f6adbabb4 100755
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -401,6 +401,10 @@
"config-entry-picker": {
"config_entry": "Integration"
},
+ "coversation-agent-picker": {
+ "conversation_agent": "Conversation agent",
+ "default": "Default agent ({default})"
+ },
"theme-picker": {
"theme": "Theme",
"no_theme": "No theme"