mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Open assist from dashboard (#16829)
This commit is contained in:
parent
1cf24ffc8d
commit
1cb1bcf274
@ -152,6 +152,12 @@ export interface MoreInfoActionConfig extends BaseActionConfig {
|
|||||||
action: "more-info";
|
action: "more-info";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AssistActionConfig extends BaseActionConfig {
|
||||||
|
action: "assist";
|
||||||
|
pipeline_id?: string;
|
||||||
|
start_listening?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface NoActionConfig extends BaseActionConfig {
|
export interface NoActionConfig extends BaseActionConfig {
|
||||||
action: "none";
|
action: "none";
|
||||||
}
|
}
|
||||||
@ -180,6 +186,7 @@ export type ActionConfig =
|
|||||||
| NavigateActionConfig
|
| NavigateActionConfig
|
||||||
| UrlActionConfig
|
| UrlActionConfig
|
||||||
| MoreInfoActionConfig
|
| MoreInfoActionConfig
|
||||||
|
| AssistActionConfig
|
||||||
| NoActionConfig
|
| NoActionConfig
|
||||||
| CustomActionConfig;
|
| CustomActionConfig;
|
||||||
|
|
||||||
|
@ -24,10 +24,10 @@ import { stopPropagation } from "../../common/dom/stop_propagation";
|
|||||||
import "../../components/ha-button";
|
import "../../components/ha-button";
|
||||||
import "../../components/ha-button-menu";
|
import "../../components/ha-button-menu";
|
||||||
import "../../components/ha-dialog";
|
import "../../components/ha-dialog";
|
||||||
|
import "../../components/ha-dialog-header";
|
||||||
import "../../components/ha-icon-button";
|
import "../../components/ha-icon-button";
|
||||||
import "../../components/ha-list-item";
|
import "../../components/ha-list-item";
|
||||||
import "../../components/ha-textfield";
|
import "../../components/ha-textfield";
|
||||||
import "../../components/ha-dialog-header";
|
|
||||||
import type { HaTextField } from "../../components/ha-textfield";
|
import type { HaTextField } from "../../components/ha-textfield";
|
||||||
import {
|
import {
|
||||||
AssistPipeline,
|
AssistPipeline,
|
||||||
@ -41,6 +41,7 @@ import type { HomeAssistant } from "../../types";
|
|||||||
import { AudioRecorder } from "../../util/audio-recorder";
|
import { AudioRecorder } from "../../util/audio-recorder";
|
||||||
import { documentationUrl } from "../../util/documentation-url";
|
import { documentationUrl } from "../../util/documentation-url";
|
||||||
import { showAlertDialog } from "../generic/show-dialog-box";
|
import { showAlertDialog } from "../generic/show-dialog-box";
|
||||||
|
import { VoiceCommandDialogParams } from "./show-ha-voice-command-dialog";
|
||||||
|
|
||||||
interface Message {
|
interface Message {
|
||||||
who: string;
|
who: string;
|
||||||
@ -82,7 +83,13 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
|
|
||||||
private _stt_binary_handler_id?: number | null;
|
private _stt_binary_handler_id?: number | null;
|
||||||
|
|
||||||
public async showDialog(): Promise<void> {
|
private _pipelinePromise?: Promise<AssistPipeline>;
|
||||||
|
|
||||||
|
public async showDialog(params?: VoiceCommandDialogParams): Promise<void> {
|
||||||
|
if (params?.pipeline_id) {
|
||||||
|
this._pipelineId = params?.pipeline_id;
|
||||||
|
}
|
||||||
|
|
||||||
this._conversation = [
|
this._conversation = [
|
||||||
{
|
{
|
||||||
who: "hass",
|
who: "hass",
|
||||||
@ -92,6 +99,11 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
this._opened = true;
|
this._opened = true;
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._scrollMessagesBottom();
|
this._scrollMessagesBottom();
|
||||||
|
|
||||||
|
await this._pipelinePromise;
|
||||||
|
if (params?.start_listening && this._pipeline?.stt_engine) {
|
||||||
|
this._toggleListening();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async closeDialog(): Promise<void> {
|
public async closeDialog(): Promise<void> {
|
||||||
@ -230,7 +242,7 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
<div class="listening-icon">
|
<div class="listening-icon">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiMicrophone}
|
.path=${mdiMicrophone}
|
||||||
@click=${this._toggleListening}
|
@click=${this._handleListeningButton}
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.voice_command.start_listening"
|
"ui.dialogs.voice_command.start_listening"
|
||||||
)}
|
)}
|
||||||
@ -275,7 +287,8 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
|
|
||||||
private async _getPipeline() {
|
private async _getPipeline() {
|
||||||
try {
|
try {
|
||||||
this._pipeline = await getAssistPipeline(this.hass, this._pipelineId);
|
this._pipelinePromise = getAssistPipeline(this.hass, this._pipelineId);
|
||||||
|
this._pipeline = await this._pipelinePromise;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e.code === "not_found") {
|
if (e.code === "not_found") {
|
||||||
this._pipelineId = undefined;
|
this._pipelineId = undefined;
|
||||||
@ -392,9 +405,13 @@ export class HaVoiceCommandDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _toggleListening(ev) {
|
private _handleListeningButton(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
this._toggleListening();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleListening() {
|
||||||
const supportsMicrophone = AudioRecorder.isSupported;
|
const supportsMicrophone = AudioRecorder.isSupported;
|
||||||
if (!supportsMicrophone) {
|
if (!supportsMicrophone) {
|
||||||
this._showNotSupportedMessage();
|
this._showNotSupportedMessage();
|
||||||
|
@ -3,19 +3,29 @@ import { HomeAssistant } from "../../types";
|
|||||||
|
|
||||||
const loadVoiceCommandDialog = () => import("./ha-voice-command-dialog");
|
const loadVoiceCommandDialog = () => import("./ha-voice-command-dialog");
|
||||||
|
|
||||||
|
export interface VoiceCommandDialogParams {
|
||||||
|
pipeline_id?: string;
|
||||||
|
start_listening?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const showVoiceCommandDialog = (
|
export const showVoiceCommandDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant,
|
||||||
|
dialogParams?: VoiceCommandDialogParams
|
||||||
): void => {
|
): void => {
|
||||||
if (hass.auth.external?.config.hasAssist) {
|
if (hass.auth.external?.config.hasAssist) {
|
||||||
hass.auth.external!.fireMessage({
|
hass.auth.external!.fireMessage({
|
||||||
type: "assist/show",
|
type: "assist/show",
|
||||||
|
payload: {
|
||||||
|
pipeline_id: dialogParams?.pipeline_id,
|
||||||
|
start_listening: dialogParams?.start_listening,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "ha-voice-command-dialog",
|
dialogTag: "ha-voice-command-dialog",
|
||||||
dialogImport: loadVoiceCommandDialog,
|
dialogImport: loadVoiceCommandDialog,
|
||||||
dialogParams: {},
|
dialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@ import { forwardHaptic } from "../../../data/haptics";
|
|||||||
import { domainToName } from "../../../data/integration";
|
import { domainToName } from "../../../data/integration";
|
||||||
import { ActionConfig } from "../../../data/lovelace";
|
import { ActionConfig } from "../../../data/lovelace";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
import { toggleEntity } from "./entity/toggle-entity";
|
import { toggleEntity } from "./entity/toggle-entity";
|
||||||
@ -155,6 +156,13 @@ export const handleAction = async (
|
|||||||
forwardHaptic("light");
|
forwardHaptic("light");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "assist": {
|
||||||
|
showVoiceCommandDialog(node, hass, {
|
||||||
|
start_listening: actionConfig.start_listening,
|
||||||
|
pipeline_id: actionConfig.pipeline_id,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "fire-dom-event": {
|
case "fire-dom-event": {
|
||||||
fireEvent(node, "ll-custom", actionConfig);
|
fireEvent(node, "ll-custom", actionConfig);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import { customElement, property } from "lit/decorators";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
|
import "../../../components/ha-assist-pipeline-picker";
|
||||||
|
import { HaFormSchema, SchemaUnion } from "../../../components/ha-form/types";
|
||||||
import "../../../components/ha-help-tooltip";
|
import "../../../components/ha-help-tooltip";
|
||||||
import "../../../components/ha-navigation-picker";
|
import "../../../components/ha-navigation-picker";
|
||||||
import "../../../components/ha-service-control";
|
import "../../../components/ha-service-control";
|
||||||
@ -24,9 +26,31 @@ const DEFAULT_ACTIONS: UiAction[] = [
|
|||||||
"navigate",
|
"navigate",
|
||||||
"url",
|
"url",
|
||||||
"call-service",
|
"call-service",
|
||||||
|
"assist",
|
||||||
"none",
|
"none",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const ASSIST_SCHEMA = [
|
||||||
|
{
|
||||||
|
type: "grid",
|
||||||
|
name: "",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "pipeline_id",
|
||||||
|
selector: {
|
||||||
|
assist_pipeline: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "start_listening",
|
||||||
|
selector: {
|
||||||
|
boolean: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as const satisfies readonly HaFormSchema[];
|
||||||
|
|
||||||
@customElement("hui-action-editor")
|
@customElement("hui-action-editor")
|
||||||
export class HuiActionEditor extends LitElement {
|
export class HuiActionEditor extends LitElement {
|
||||||
@property() public config?: ActionConfig;
|
@property() public config?: ActionConfig;
|
||||||
@ -101,7 +125,7 @@ export class HuiActionEditor extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<ha-help-tooltip .label=${this.tooltipText}></ha-help-tooltip>
|
<ha-help-tooltip .label=${this.tooltipText}></ha-help-tooltip>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
${this.config?.action === "navigate"
|
${this.config?.action === "navigate"
|
||||||
? html`
|
? html`
|
||||||
@ -114,7 +138,7 @@ export class HuiActionEditor extends LitElement {
|
|||||||
@value-changed=${this._navigateValueChanged}
|
@value-changed=${this._navigateValueChanged}
|
||||||
></ha-navigation-picker>
|
></ha-navigation-picker>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
${this.config?.action === "url"
|
${this.config?.action === "url"
|
||||||
? html`
|
? html`
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
@ -126,7 +150,7 @@ export class HuiActionEditor extends LitElement {
|
|||||||
@input=${this._valueChanged}
|
@input=${this._valueChanged}
|
||||||
></ha-textfield>
|
></ha-textfield>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
${this.config?.action === "call-service"
|
${this.config?.action === "call-service"
|
||||||
? html`
|
? html`
|
||||||
<ha-service-control
|
<ha-service-control
|
||||||
@ -137,7 +161,19 @@ export class HuiActionEditor extends LitElement {
|
|||||||
@value-changed=${this._serviceValueChanged}
|
@value-changed=${this._serviceValueChanged}
|
||||||
></ha-service-control>
|
></ha-service-control>
|
||||||
`
|
`
|
||||||
: ""}
|
: nothing}
|
||||||
|
${this.config?.action === "assist"
|
||||||
|
? html`
|
||||||
|
<ha-form
|
||||||
|
.hass=${this.hass}
|
||||||
|
.schema=${ASSIST_SCHEMA}
|
||||||
|
.data=${this.config}
|
||||||
|
.computeLabel=${this._computeFormLabel}
|
||||||
|
@value-changed=${this._formValueChanged}
|
||||||
|
>
|
||||||
|
</ha-form>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +218,7 @@ export class HuiActionEditor extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const target = ev.target! as EditorTarget;
|
const target = ev.target! as EditorTarget;
|
||||||
const value = ev.target.value;
|
const value = ev.target.value ?? ev.target.checked;
|
||||||
if (this[`_${target.configValue}`] === value) {
|
if (this[`_${target.configValue}`] === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -193,6 +229,21 @@ export class HuiActionEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _formValueChanged(ev): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const value = ev.detail.value;
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeFormLabel(schema: SchemaUnion<typeof ASSIST_SCHEMA>) {
|
||||||
|
return this.hass?.localize(
|
||||||
|
`ui.panel.lovelace.editor.action-editor.${schema.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private _serviceValueChanged(ev: CustomEvent) {
|
private _serviceValueChanged(ev: CustomEvent) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const value = {
|
const value = {
|
||||||
@ -240,17 +291,25 @@ export class HuiActionEditor extends LitElement {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
ha-service-control,
|
ha-service-control,
|
||||||
ha-navigation-picker {
|
ha-navigation-picker,
|
||||||
|
ha-form {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
ha-textfield,
|
ha-textfield,
|
||||||
ha-service-control,
|
ha-service-control,
|
||||||
ha-navigation-picker {
|
ha-navigation-picker,
|
||||||
|
ha-form {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
ha-service-control {
|
ha-service-control {
|
||||||
--service-control-padding: 0;
|
--service-control-padding: 0;
|
||||||
}
|
}
|
||||||
|
ha-formfield {
|
||||||
|
display: flex;
|
||||||
|
height: 56px;
|
||||||
|
align-items: center;
|
||||||
|
--mdc-typography-body2-font-size: 1em;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,12 @@ const actionConfigStructNavigate = object({
|
|||||||
confirmation: optional(actionConfigStructConfirmation),
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const actionConfigStructAssist = type({
|
||||||
|
action: literal("assist"),
|
||||||
|
pipeline_id: optional(string()),
|
||||||
|
start_listening: optional(boolean()),
|
||||||
|
});
|
||||||
|
|
||||||
const actionConfigStructCustom = type({
|
const actionConfigStructCustom = type({
|
||||||
action: literal("fire-dom-event"),
|
action: literal("fire-dom-event"),
|
||||||
});
|
});
|
||||||
@ -63,6 +69,7 @@ export const actionConfigStructType = object({
|
|||||||
"call-service",
|
"call-service",
|
||||||
"url",
|
"url",
|
||||||
"navigate",
|
"navigate",
|
||||||
|
"assist",
|
||||||
]),
|
]),
|
||||||
confirmation: optional(actionConfigStructConfirmation),
|
confirmation: optional(actionConfigStructConfirmation),
|
||||||
});
|
});
|
||||||
@ -82,6 +89,9 @@ export const actionConfigStruct = dynamic<any>((value) => {
|
|||||||
case "url": {
|
case "url": {
|
||||||
return actionConfigStructUrl;
|
return actionConfigStructUrl;
|
||||||
}
|
}
|
||||||
|
case "assist": {
|
||||||
|
return actionConfigStructAssist;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4417,12 +4417,15 @@
|
|||||||
"action-editor": {
|
"action-editor": {
|
||||||
"navigation_path": "Navigation Path",
|
"navigation_path": "Navigation Path",
|
||||||
"url_path": "URL Path",
|
"url_path": "URL Path",
|
||||||
|
"start_listening": "Start listening",
|
||||||
|
"pipeline_id": "Assistant",
|
||||||
"actions": {
|
"actions": {
|
||||||
"default_action": "Default Action",
|
"default_action": "Default Action",
|
||||||
"call-service": "Call Service",
|
"call-service": "Call Service",
|
||||||
"more-info": "More Info",
|
"more-info": "More Info",
|
||||||
"toggle": "Toggle",
|
"toggle": "Toggle",
|
||||||
"navigate": "Navigate",
|
"navigate": "Navigate",
|
||||||
|
"assist": "Assist",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"none": "No Action"
|
"none": "No Action"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user