mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 00:06:35 +00:00
Improv external flow (#22878)
* WIP improv external flow * Update external_messaging.ts * use name instead, start flow * make copy * Update ha-config-integrations-dashboard.ts * Update * rename command * Use a map instead of array for deduping
This commit is contained in:
parent
2899388395
commit
6b8068a22a
@ -13,6 +13,7 @@ import type {
|
||||
EMIncomingMessageBarCodeScanAborted,
|
||||
EMIncomingMessageBarCodeScanResult,
|
||||
EMIncomingMessageCommands,
|
||||
ImprovDiscoveredDevice,
|
||||
} from "./external_messaging";
|
||||
|
||||
const barCodeListeners = new Set<
|
||||
@ -113,6 +114,22 @@ const handleExternalMessage = (
|
||||
success: true,
|
||||
result: null,
|
||||
});
|
||||
} else if (msg.command === "improv/discovered_device") {
|
||||
fireEvent(window, "improv-discovered-device", msg.payload);
|
||||
bus.fireMessage({
|
||||
id: msg.id,
|
||||
type: "result",
|
||||
success: true,
|
||||
result: null,
|
||||
});
|
||||
} else if (msg.command === "improv/device_setup_done") {
|
||||
fireEvent(window, "improv-device-setup-done");
|
||||
bus.fireMessage({
|
||||
id: msg.id,
|
||||
type: "result",
|
||||
success: true,
|
||||
result: null,
|
||||
});
|
||||
} else if (msg.command === "bar_code/scan_result") {
|
||||
barCodeListeners.forEach((listener) => listener(msg));
|
||||
bus.fireMessage({
|
||||
@ -135,3 +152,10 @@ const handleExternalMessage = (
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"improv-discovered-device": ImprovDiscoveredDevice;
|
||||
"improv-device-setup-done": undefined;
|
||||
}
|
||||
}
|
||||
|
@ -134,10 +134,18 @@ interface EMOutgoingMessageAssistShow extends EMMessage {
|
||||
start_listening: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface EMOutgoingMessageImprovScan extends EMMessage {
|
||||
type: "improv/scan";
|
||||
}
|
||||
|
||||
interface EMOutgoingMessageImprovConfigureDevice extends EMMessage {
|
||||
type: "improv/configure_device";
|
||||
payload: {
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface EMOutgoingMessageThreadStoreInPlatformKeychain extends EMMessage {
|
||||
type: "thread/store_in_platform_keychain";
|
||||
payload: {
|
||||
@ -167,7 +175,8 @@ type EMOutgoingMessageWithoutAnswer =
|
||||
| EMOutgoingMessageTagWrite
|
||||
| EMOutgoingMessageThemeUpdate
|
||||
| EMOutgoingMessageThreadStoreInPlatformKeychain
|
||||
| EMOutgoingMessageImprovScan;
|
||||
| EMOutgoingMessageImprovScan
|
||||
| EMOutgoingMessageImprovConfigureDevice;
|
||||
|
||||
interface EMIncomingMessageRestart {
|
||||
id: number;
|
||||
@ -237,6 +246,23 @@ export interface EMIncomingMessageBarCodeScanAborted {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ImprovDiscoveredDevice {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface EMIncomingMessageImprovDeviceDiscovered extends EMMessage {
|
||||
id: number;
|
||||
type: "command";
|
||||
command: "improv/discovered_device";
|
||||
payload: ImprovDiscoveredDevice;
|
||||
}
|
||||
|
||||
interface EMIncomingMessageImprovDeviceSetupDone extends EMMessage {
|
||||
id: number;
|
||||
type: "command";
|
||||
command: "improv/device_setup_done";
|
||||
}
|
||||
|
||||
export type EMIncomingMessageCommands =
|
||||
| EMIncomingMessageRestart
|
||||
| EMIncomingMessageShowNotifications
|
||||
@ -244,7 +270,9 @@ export type EMIncomingMessageCommands =
|
||||
| EMIncomingMessageShowSidebar
|
||||
| EMIncomingMessageShowAutomationEditor
|
||||
| EMIncomingMessageBarCodeScanResult
|
||||
| EMIncomingMessageBarCodeScanAborted;
|
||||
| EMIncomingMessageBarCodeScanAborted
|
||||
| EMIncomingMessageImprovDeviceDiscovered
|
||||
| EMIncomingMessageImprovDeviceSetupDone;
|
||||
|
||||
type EMIncomingMessage =
|
||||
| EMMessageResultSuccess
|
||||
|
@ -124,6 +124,17 @@ export class HaConfigFlowCard extends LitElement {
|
||||
}
|
||||
|
||||
private _continueFlow() {
|
||||
if (this.flow.flow_id === "external") {
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "improv/configure_device",
|
||||
payload: {
|
||||
name:
|
||||
this.flow.localized_title ||
|
||||
this.flow.context.title_placeholders.name,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
showConfigFlowDialog(this, {
|
||||
continueFlowId: this.flow.flow_id,
|
||||
dialogClosedCallback: () => {
|
||||
|
@ -69,6 +69,7 @@ import type { HaIntegrationCard } from "./ha-integration-card";
|
||||
import "./ha-integration-overflow-menu";
|
||||
import { showAddIntegrationDialog } from "./show-add-integration-dialog";
|
||||
import { fetchEntitySourcesWithCache } from "../../../data/entity_sources";
|
||||
import type { ImprovDiscoveredDevice } from "../../../external_app/external_messaging";
|
||||
|
||||
export interface ConfigEntryExtended extends Omit<ConfigEntry, "entry_id"> {
|
||||
entry_id?: string;
|
||||
@ -105,6 +106,9 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
@property({ attribute: false })
|
||||
public configEntriesInProgress?: DataEntryFlowProgressExtended[];
|
||||
|
||||
@state() private _improvDiscovered: Map<string, ImprovDiscoveredDevice> =
|
||||
new Map();
|
||||
|
||||
@state()
|
||||
private _entityRegistryEntries: EntityRegistryEntry[] = [];
|
||||
|
||||
@ -131,6 +135,18 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
[integration: string]: IntegrationLogInfo;
|
||||
};
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener(
|
||||
"improv-discovered-device",
|
||||
this._handleImprovDiscovered
|
||||
);
|
||||
window.removeEventListener(
|
||||
"improv-device-setup-done",
|
||||
this._reScanImprovDevices
|
||||
);
|
||||
}
|
||||
|
||||
public hassSubscribe(): Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
|
||||
return [
|
||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||
@ -244,8 +260,38 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
private _filterConfigEntriesInProgress = memoizeOne(
|
||||
(
|
||||
configEntriesInProgress: DataEntryFlowProgressExtended[],
|
||||
improvDiscovered: Map<string, ImprovDiscoveredDevice>,
|
||||
filter?: string
|
||||
): DataEntryFlowProgressExtended[] => {
|
||||
let inProgress = [...configEntriesInProgress];
|
||||
|
||||
const improvDiscoveredArray = Array.from(improvDiscovered.values());
|
||||
|
||||
if (improvDiscoveredArray.length) {
|
||||
// filter out native flows that have been discovered by both mobile and local bluetooth
|
||||
inProgress = inProgress.filter(
|
||||
(flow) =>
|
||||
!improvDiscoveredArray.some(
|
||||
(discovered) => discovered.name === flow.localized_title
|
||||
)
|
||||
);
|
||||
|
||||
// add mobile flows to the list
|
||||
improvDiscovered.forEach((discovered) => {
|
||||
inProgress.push({
|
||||
flow_id: "external",
|
||||
handler: "improv_ble",
|
||||
context: {
|
||||
title_placeholders: {
|
||||
name: discovered.name,
|
||||
},
|
||||
},
|
||||
step_id: "bluetooth_confirm",
|
||||
localized_title: discovered.name,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
let filteredEntries: DataEntryFlowProgressExtended[];
|
||||
if (filter) {
|
||||
const options: IFuseOptions<DataEntryFlowProgressExtended> = {
|
||||
@ -255,12 +301,12 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
threshold: 0.2,
|
||||
getFn: getStripDiacriticsFn,
|
||||
};
|
||||
const fuse = new Fuse(configEntriesInProgress, options);
|
||||
const fuse = new Fuse(inProgress, options);
|
||||
filteredEntries = fuse
|
||||
.search(stripDiacritics(filter))
|
||||
.map((result) => result.item);
|
||||
} else {
|
||||
filteredEntries = configEntriesInProgress;
|
||||
filteredEntries = inProgress;
|
||||
}
|
||||
return filteredEntries.sort((a, b) =>
|
||||
caseInsensitiveStringCompare(
|
||||
@ -280,6 +326,8 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
this._handleAdd();
|
||||
}
|
||||
this._scanUSBDevices();
|
||||
this._scanImprovDevices();
|
||||
|
||||
if (isComponentLoaded(this.hass, "diagnostics")) {
|
||||
fetchDiagnosticHandlers(this.hass).then((infos) => {
|
||||
const handlers = {};
|
||||
@ -334,6 +382,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
const configEntriesInProgress = this._filterConfigEntriesInProgress(
|
||||
this.configEntriesInProgress,
|
||||
this._improvDiscovered,
|
||||
this._filter
|
||||
);
|
||||
|
||||
@ -608,6 +657,43 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
await scanUSBDevices(this.hass);
|
||||
}
|
||||
|
||||
private _scanImprovDevices() {
|
||||
if (!this.hass.auth.external?.config.canSetupImprov) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"improv-discovered-device",
|
||||
this._handleImprovDiscovered
|
||||
);
|
||||
|
||||
window.addEventListener(
|
||||
"improv-device-setup-done",
|
||||
this._reScanImprovDevices
|
||||
);
|
||||
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "improv/scan",
|
||||
});
|
||||
}
|
||||
|
||||
private _reScanImprovDevices = () => {
|
||||
if (!this.hass.auth.external?.config.canSetupImprov) {
|
||||
return;
|
||||
}
|
||||
this._improvDiscovered = new Map();
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "improv/scan",
|
||||
});
|
||||
};
|
||||
|
||||
private _handleImprovDiscovered = (ev) => {
|
||||
this._fetchManifests(["improv_ble"]);
|
||||
this._improvDiscovered.set(ev.detail.name, ev.detail);
|
||||
// copy for memoize and reactive updates
|
||||
this._improvDiscovered = new Map(Array.from(this._improvDiscovered));
|
||||
};
|
||||
|
||||
private async _fetchEntitySources() {
|
||||
const entitySources = await fetchEntitySourcesWithCache(this.hass);
|
||||
|
||||
@ -657,6 +743,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
|
||||
private _handleFlowUpdated() {
|
||||
getConfigFlowInProgressCollection(this.hass.connection).refresh();
|
||||
this._reScanImprovDevices();
|
||||
this._fetchManifests();
|
||||
}
|
||||
|
||||
@ -664,11 +751,6 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
|
||||
showAddIntegrationDialog(this, {
|
||||
initialFilter: this._filter,
|
||||
});
|
||||
if (this.hass.auth.external?.config.canSetupImprov) {
|
||||
this.hass.auth.external!.fireMessage({
|
||||
type: "improv/scan",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user