diff --git a/src/data/supported_brands.ts b/src/data/supported_brands.ts new file mode 100644 index 0000000000..ef5994e871 --- /dev/null +++ b/src/data/supported_brands.ts @@ -0,0 +1,8 @@ +import type { HomeAssistant } from "../types"; + +export type SupportedBrandHandler = Record; + +export const getSupportedBrands = (hass: HomeAssistant) => + hass.callWS>({ + type: "supported_brands", + }); diff --git a/src/dialogs/config-flow/show-dialog-config-flow.ts b/src/dialogs/config-flow/show-dialog-config-flow.ts index 53c92fd214..e987d6be40 100644 --- a/src/dialogs/config-flow/show-dialog-config-flow.ts +++ b/src/dialogs/config-flow/show-dialog-config-flow.ts @@ -7,6 +7,7 @@ import { handleConfigFlowStep, } from "../../data/config_flow"; import { domainToName } from "../../data/integration"; +import { getSupportedBrands } from "../../data/supported_brands"; import { DataEntryFlowDialogParams, loadDataEntryFlowDialog, @@ -22,12 +23,14 @@ export const showConfigFlowDialog = ( showFlowDialog(element, dialogParams, { loadDevicesAndAreas: true, getFlowHandlers: async (hass) => { - const [integrations, helpers] = await Promise.all([ + const [integrations, helpers, supportedBrands] = await Promise.all([ getConfigFlowHandlers(hass, "integration"), getConfigFlowHandlers(hass, "helper"), + getSupportedBrands(hass), hass.loadBackendTranslation("title", undefined, true), ]); - return { integrations, helpers }; + + return { integrations, helpers, supportedBrands }; }, createFlow: async (hass, handler) => { const [step] = await Promise.all([ diff --git a/src/dialogs/config-flow/show-dialog-data-entry-flow.ts b/src/dialogs/config-flow/show-dialog-data-entry-flow.ts index f6abcedc46..380faaf064 100644 --- a/src/dialogs/config-flow/show-dialog-data-entry-flow.ts +++ b/src/dialogs/config-flow/show-dialog-data-entry-flow.ts @@ -10,12 +10,14 @@ import { DataEntryFlowStepMenu, DataEntryFlowStepProgress, } from "../../data/data_entry_flow"; -import { IntegrationManifest } from "../../data/integration"; -import { HomeAssistant } from "../../types"; +import type { IntegrationManifest } from "../../data/integration"; +import type { SupportedBrandHandler } from "../../data/supported_brands"; +import type { HomeAssistant } from "../../types"; export interface FlowHandlers { integrations: string[]; helpers: string[]; + supportedBrands: Record; } export interface FlowConfig { loadDevicesAndAreas: boolean; diff --git a/src/dialogs/config-flow/step-flow-pick-flow.ts b/src/dialogs/config-flow/step-flow-pick-flow.ts index cb942fe3da..fd24243a0b 100644 --- a/src/dialogs/config-flow/step-flow-pick-flow.ts +++ b/src/dialogs/config-flow/step-flow-pick-flow.ts @@ -1,5 +1,6 @@ import "@polymer/paper-item"; import "@polymer/paper-item/paper-icon-item"; +import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item-body"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; diff --git a/src/dialogs/config-flow/step-flow-pick-handler.ts b/src/dialogs/config-flow/step-flow-pick-handler.ts index 797ae3c4af..6812d118ec 100644 --- a/src/dialogs/config-flow/step-flow-pick-handler.ts +++ b/src/dialogs/config-flow/step-flow-pick-handler.ts @@ -15,18 +15,19 @@ import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { fireEvent } from "../../common/dom/fire_event"; import { navigate } from "../../common/navigate"; -import "../../components/search-input"; import { caseInsensitiveStringCompare } from "../../common/string/compare"; import { LocalizeFunc } from "../../common/translations/localize"; import "../../components/ha-icon-next"; +import "../../components/search-input"; import { getConfigEntries } from "../../data/config_entries"; import { domainToName } from "../../data/integration"; import { showZWaveJSAddNodeDialog } from "../../panels/config/integrations/integration-panels/zwave_js/show-dialog-zwave_js-add-node"; import { HomeAssistant } from "../../types"; import { brandsUrl } from "../../util/brands-url"; import { documentationUrl } from "../../util/documentation-url"; -import { configFlowContentStyles } from "./styles"; +import { showConfirmationDialog } from "../generic/show-dialog-box"; import { FlowHandlers } from "./show-dialog-data-entry-flow"; +import { configFlowContentStyles } from "./styles"; interface HandlerObj { name: string; @@ -35,6 +36,10 @@ interface HandlerObj { is_helper?: boolean; } +interface SupportedBrandObj extends HandlerObj { + supported_flows: string[]; +} + declare global { // for fire event interface HASSDomEvents { @@ -63,11 +68,22 @@ class StepFlowPickHandler extends LitElement { h: FlowHandlers, filter?: string, _localize?: LocalizeFunc - ): [HandlerObj[], HandlerObj[]] => { - const integrations: HandlerObj[] = h.integrations.map((handler) => ({ - name: domainToName(this.hass.localize, handler), - slug: handler, - })); + ): [(HandlerObj | SupportedBrandObj)[], HandlerObj[]] => { + const integrations: (HandlerObj | SupportedBrandObj)[] = + h.integrations.map((handler) => ({ + name: domainToName(this.hass.localize, handler), + slug: handler, + })); + + for (const [domain, domainBrands] of Object.entries(h.supportedBrands)) { + for (const [slug, name] of Object.entries(domainBrands)) { + integrations.push({ + slug, + name, + supported_flows: [domain], + }); + } + } if (filter) { const options: Fuse.IFuseOptions = { @@ -238,27 +254,10 @@ class StepFlowPickHandler extends LitElement { } private async _handlerPicked(ev) { - const handler: HandlerObj = ev.currentTarget.handler; + const handler: HandlerObj | SupportedBrandObj = ev.currentTarget.handler; if (handler.is_add) { - if (handler.slug === "zwave_js") { - const entries = await getConfigEntries(this.hass, { - domain: "zwave_js", - }); - - if (!entries.length) { - return; - } - - showZWaveJSAddNodeDialog(this, { - entry_id: entries[0].entry_id, - }); - } else if (handler.slug === "zha") { - navigate("/config/zha/add"); - } - - // This closes dialog. - fireEvent(this, "flow-update"); + this._handleAddPicked(handler.slug); return; } @@ -269,11 +268,84 @@ class StepFlowPickHandler extends LitElement { return; } + if ("supported_flows" in handler) { + const slug = handler.supported_flows[0]; + + showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_flow.supported_brand_flow", + { + supported_brand: handler.name, + flow_domain_name: domainToName(this.hass.localize, slug), + } + ), + confirm: () => { + if (["zha", "zwave_js"].includes(slug)) { + this._handleAddPicked(slug); + return; + } + + fireEvent(this, "handler-picked", { + handler: slug, + }); + }, + }); + + return; + } + fireEvent(this, "handler-picked", { handler: handler.slug, }); } + private async _handleAddPicked(slug: string): Promise { + if (slug === "zwave_js") { + const entries = await getConfigEntries(this.hass, { + domain: "zwave_js", + }); + + if (!entries.length) { + // If the component isn't loaded, ask them to load the integration first + showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_flow.missing_zwave_js" + ), + confirm: () => { + fireEvent(this, "handler-picked", { + handler: "zwave_js", + }); + }, + }); + return; + } + + showZWaveJSAddNodeDialog(this, { + entry_id: entries[0].entry_id, + }); + } else if (slug === "zha") { + // If the component isn't loaded, ask them to load the integration first + if (!isComponentLoaded(this.hass, "zha")) { + showConfirmationDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_flow.missing_zha" + ), + confirm: () => { + fireEvent(this, "handler-picked", { + handler: "zha", + }); + }, + }); + return; + } + + navigate("/config/zha/add"); + } + + // This closes dialog. + fireEvent(this, "flow-update"); + } + private _maybeSubmit(ev: KeyboardEvent) { if (ev.key !== "Enter") { return; diff --git a/src/translations/en.json b/src/translations/en.json index cc53fac488..d916459869 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -2850,7 +2850,10 @@ "error": "Error", "could_not_load": "Config flow could not be loaded", "not_loaded": "The integration could not be loaded, try to restart Home Assistant.", - "missing_credentials": "Setting up {integration} requires configuring application credentials. Do you want to do that now?" + "missing_credentials": "Setting up {integration} requires configuring application credentials. Do you want to do that now?", + "supported_brand_flow": "Support for {supported_brand} devices is provided by {flow_domain_name}. Do you want to continue?", + "missing_zwave_js": "To add a Z-Wave device, you first need to set up the Z-Wave integration. Do you want to do that now?", + "missing_zha": "To add a Zigbee device, you first need to set up the Zigbee Home Automation integration. Do you want to do that now?" } }, "users": {