mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
Migrate integrations page to TypeScript (#3500)
This commit is contained in:
parent
6c109c15ef
commit
355e3d7911
@ -1,8 +1,4 @@
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { createCollection } from "home-assistant-js-websocket";
|
|
||||||
import { debounce } from "../common/util/debounce";
|
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
|
||||||
import { DataEntryFlowStep, DataEntryFlowProgress } from "./data_entry_flow";
|
|
||||||
|
|
||||||
export interface ConfigEntry {
|
export interface ConfigEntry {
|
||||||
entry_id: string;
|
entry_id: string;
|
||||||
@ -14,114 +10,10 @@ export interface ConfigEntry {
|
|||||||
supports_options: boolean;
|
supports_options: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
|
|
||||||
handler,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const fetchConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>(
|
|
||||||
"GET",
|
|
||||||
`config/config_entries/flow/${flowId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
export const handleConfigFlowStep = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
flowId: string,
|
|
||||||
data: { [key: string]: any }
|
|
||||||
) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>(
|
|
||||||
"POST",
|
|
||||||
`config/config_entries/flow/${flowId}`,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
export const deleteConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
|
||||||
hass.callApi("DELETE", `config/config_entries/flow/${flowId}`);
|
|
||||||
|
|
||||||
export const getConfigFlowsInProgress = (hass: HomeAssistant) =>
|
|
||||||
hass.callApi<DataEntryFlowProgress[]>("GET", "config/config_entries/flow");
|
|
||||||
|
|
||||||
export const getConfigFlowHandlers = (hass: HomeAssistant) =>
|
|
||||||
hass.callApi<string[]>("GET", "config/config_entries/flow_handlers");
|
|
||||||
|
|
||||||
const fetchConfigFlowInProgress = (conn) =>
|
|
||||||
conn.sendMessagePromise({
|
|
||||||
type: "config/entity_registry/list",
|
|
||||||
});
|
|
||||||
|
|
||||||
const subscribeConfigFlowInProgressUpdates = (conn, store) =>
|
|
||||||
debounce(
|
|
||||||
conn.subscribeEvents(
|
|
||||||
() =>
|
|
||||||
fetchConfigFlowInProgress(conn).then((flows) =>
|
|
||||||
store.setState(flows, true)
|
|
||||||
),
|
|
||||||
500,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"config_entry_discovered"
|
|
||||||
);
|
|
||||||
|
|
||||||
export const subscribeConfigFlowInProgress = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
onChange: (flows: DataEntryFlowProgress[]) => void
|
|
||||||
) =>
|
|
||||||
createCollection<DataEntryFlowProgress[]>(
|
|
||||||
"_configFlowProgress",
|
|
||||||
fetchConfigFlowInProgress,
|
|
||||||
subscribeConfigFlowInProgressUpdates,
|
|
||||||
hass.connection,
|
|
||||||
onChange
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getConfigEntries = (hass: HomeAssistant) =>
|
export const getConfigEntries = (hass: HomeAssistant) =>
|
||||||
hass.callApi<ConfigEntry[]>("GET", "config/config_entries/entry");
|
hass.callApi<ConfigEntry[]>("GET", "config/config_entries/entry");
|
||||||
|
|
||||||
export const localizeConfigFlowTitle = (
|
export const deleteConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
|
||||||
localize: LocalizeFunc,
|
hass.callApi<{
|
||||||
flow: DataEntryFlowProgress
|
require_restart: boolean;
|
||||||
) => {
|
}>("DELETE", `config/config_entries/entry/${configEntryId}`);
|
||||||
const placeholders = flow.context.title_placeholders || {};
|
|
||||||
const placeholderKeys = Object.keys(placeholders);
|
|
||||||
if (placeholderKeys.length === 0) {
|
|
||||||
return localize(`component.${flow.handler}.config.title`);
|
|
||||||
}
|
|
||||||
const args: string[] = [];
|
|
||||||
placeholderKeys.forEach((key) => {
|
|
||||||
args.push(key);
|
|
||||||
args.push(placeholders[key]);
|
|
||||||
});
|
|
||||||
return localize(`component.${flow.handler}.config.flow_title`, ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Options flow
|
|
||||||
|
|
||||||
export const createOptionsFlow = (hass: HomeAssistant, handler: string) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>(
|
|
||||||
"POST",
|
|
||||||
"config/config_entries/options/flow",
|
|
||||||
{
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const fetchOptionsFlow = (hass: HomeAssistant, flowId: string) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>(
|
|
||||||
"GET",
|
|
||||||
`config/config_entries/options/flow/${flowId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
export const handleOptionsFlowStep = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
flowId: string,
|
|
||||||
data: { [key: string]: any }
|
|
||||||
) =>
|
|
||||||
hass.callApi<DataEntryFlowStep>(
|
|
||||||
"POST",
|
|
||||||
`config/config_entries/options/flow/${flowId}`,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
export const deleteOptionsFlow = (hass: HomeAssistant, flowId: string) =>
|
|
||||||
hass.callApi("DELETE", `config/config_entries/options/flow/${flowId}`);
|
|
||||||
|
83
src/data/config_flow.ts
Normal file
83
src/data/config_flow.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { DataEntryFlowStep, DataEntryFlowProgress } from "./data_entry_flow";
|
||||||
|
import { debounce } from "../common/util/debounce";
|
||||||
|
import { createCollection } from "home-assistant-js-websocket";
|
||||||
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
|
|
||||||
|
export const createConfigFlow = (hass: HomeAssistant, handler: string) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>("POST", "config/config_entries/flow", {
|
||||||
|
handler,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const fetchConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>(
|
||||||
|
"GET",
|
||||||
|
`config/config_entries/flow/${flowId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const handleConfigFlowStep = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
flowId: string,
|
||||||
|
data: { [key: string]: any }
|
||||||
|
) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>(
|
||||||
|
"POST",
|
||||||
|
`config/config_entries/flow/${flowId}`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
export const deleteConfigFlow = (hass: HomeAssistant, flowId: string) =>
|
||||||
|
hass.callApi("DELETE", `config/config_entries/flow/${flowId}`);
|
||||||
|
|
||||||
|
export const getConfigFlowsInProgress = (hass: HomeAssistant) =>
|
||||||
|
hass.callApi<DataEntryFlowProgress[]>("GET", "config/config_entries/flow");
|
||||||
|
|
||||||
|
export const getConfigFlowHandlers = (hass: HomeAssistant) =>
|
||||||
|
hass.callApi<string[]>("GET", "config/config_entries/flow_handlers");
|
||||||
|
|
||||||
|
const fetchConfigFlowInProgress = (conn) =>
|
||||||
|
conn.sendMessagePromise({
|
||||||
|
type: "config/config_entries/flow",
|
||||||
|
});
|
||||||
|
|
||||||
|
const subscribeConfigFlowInProgressUpdates = (conn, store) =>
|
||||||
|
conn.subscribeEvents(
|
||||||
|
debounce(
|
||||||
|
() =>
|
||||||
|
fetchConfigFlowInProgress(conn).then((flows) =>
|
||||||
|
store.setState(flows, true)
|
||||||
|
),
|
||||||
|
500,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
"config_entry_discovered"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const subscribeConfigFlowInProgress = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
onChange: (flows: DataEntryFlowProgress[]) => void
|
||||||
|
) =>
|
||||||
|
createCollection<DataEntryFlowProgress[]>(
|
||||||
|
"_configFlowProgress",
|
||||||
|
fetchConfigFlowInProgress,
|
||||||
|
subscribeConfigFlowInProgressUpdates,
|
||||||
|
hass.connection,
|
||||||
|
onChange
|
||||||
|
);
|
||||||
|
|
||||||
|
export const localizeConfigFlowTitle = (
|
||||||
|
localize: LocalizeFunc,
|
||||||
|
flow: DataEntryFlowProgress
|
||||||
|
) => {
|
||||||
|
const placeholders = flow.context.title_placeholders || {};
|
||||||
|
const placeholderKeys = Object.keys(placeholders);
|
||||||
|
if (placeholderKeys.length === 0) {
|
||||||
|
return localize(`component.${flow.handler}.config.title`);
|
||||||
|
}
|
||||||
|
const args: string[] = [];
|
||||||
|
placeholderKeys.forEach((key) => {
|
||||||
|
args.push(key);
|
||||||
|
args.push(placeholders[key]);
|
||||||
|
});
|
||||||
|
return localize(`component.${flow.handler}.config.flow_title`, ...args);
|
||||||
|
};
|
31
src/data/options_flow.ts
Normal file
31
src/data/options_flow.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { DataEntryFlowStep } from "./data_entry_flow";
|
||||||
|
|
||||||
|
export const createOptionsFlow = (hass: HomeAssistant, handler: string) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>(
|
||||||
|
"POST",
|
||||||
|
"config/config_entries/options/flow",
|
||||||
|
{
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const fetchOptionsFlow = (hass: HomeAssistant, flowId: string) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>(
|
||||||
|
"GET",
|
||||||
|
`config/config_entries/options/flow/${flowId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const handleOptionsFlowStep = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
flowId: string,
|
||||||
|
data: { [key: string]: any }
|
||||||
|
) =>
|
||||||
|
hass.callApi<DataEntryFlowStep>(
|
||||||
|
"POST",
|
||||||
|
`config/config_entries/options/flow/${flowId}`,
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
export const deleteOptionsFlow = (hass: HomeAssistant, flowId: string) =>
|
||||||
|
hass.callApi("DELETE", `config/config_entries/options/flow/${flowId}`);
|
@ -4,7 +4,7 @@ import {
|
|||||||
handleConfigFlowStep,
|
handleConfigFlowStep,
|
||||||
deleteConfigFlow,
|
deleteConfigFlow,
|
||||||
createConfigFlow,
|
createConfigFlow,
|
||||||
} from "../../data/config_entries";
|
} from "../../data/config_flow";
|
||||||
import { html } from "lit-element";
|
import { html } from "lit-element";
|
||||||
import { localizeKey } from "../../common/translations/localize";
|
import { localizeKey } from "../../common/translations/localize";
|
||||||
import {
|
import {
|
||||||
|
@ -3,14 +3,14 @@ import {
|
|||||||
handleOptionsFlowStep,
|
handleOptionsFlowStep,
|
||||||
deleteOptionsFlow,
|
deleteOptionsFlow,
|
||||||
createOptionsFlow,
|
createOptionsFlow,
|
||||||
ConfigEntry,
|
} from "../../data/options_flow";
|
||||||
} from "../../data/config_entries";
|
|
||||||
import { html } from "lit-element";
|
import { html } from "lit-element";
|
||||||
import { localizeKey } from "../../common/translations/localize";
|
import { localizeKey } from "../../common/translations/localize";
|
||||||
import {
|
import {
|
||||||
showFlowDialog,
|
showFlowDialog,
|
||||||
loadDataEntryFlowDialog,
|
loadDataEntryFlowDialog,
|
||||||
} from "./show-dialog-data-entry-flow";
|
} from "./show-dialog-data-entry-flow";
|
||||||
|
import { ConfigEntry } from "../../data/config_entries";
|
||||||
|
|
||||||
export const loadOptionsFlowDialog = loadDataEntryFlowDialog;
|
export const loadOptionsFlowDialog = loadDataEntryFlowDialog;
|
||||||
|
|
||||||
|
@ -14,12 +14,7 @@ import {
|
|||||||
showConfigFlowDialog,
|
showConfigFlowDialog,
|
||||||
} from "../dialogs/config-flow/show-dialog-config-flow";
|
} from "../dialogs/config-flow/show-dialog-config-flow";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import {
|
import { getConfigEntries, ConfigEntry } from "../data/config_entries";
|
||||||
getConfigFlowsInProgress,
|
|
||||||
getConfigEntries,
|
|
||||||
ConfigEntry,
|
|
||||||
localizeConfigFlowTitle,
|
|
||||||
} from "../data/config_entries";
|
|
||||||
import { compare } from "../common/string/compare";
|
import { compare } from "../common/string/compare";
|
||||||
import "./integration-badge";
|
import "./integration-badge";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
@ -28,6 +23,10 @@ import { fireEvent } from "../common/dom/fire_event";
|
|||||||
import { onboardIntegrationStep } from "../data/onboarding";
|
import { onboardIntegrationStep } from "../data/onboarding";
|
||||||
import { genClientId } from "home-assistant-js-websocket";
|
import { genClientId } from "home-assistant-js-websocket";
|
||||||
import { DataEntryFlowProgress } from "../data/data_entry_flow";
|
import { DataEntryFlowProgress } from "../data/data_entry_flow";
|
||||||
|
import {
|
||||||
|
localizeConfigFlowTitle,
|
||||||
|
getConfigFlowsInProgress,
|
||||||
|
} from "../data/config_flow";
|
||||||
|
|
||||||
@customElement("onboarding-integrations")
|
@customElement("onboarding-integrations")
|
||||||
class OnboardingIntegrations extends LitElement {
|
class OnboardingIntegrations extends LitElement {
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
loadConfigFlowDialog,
|
loadConfigFlowDialog,
|
||||||
showConfigFlowDialog,
|
showConfigFlowDialog,
|
||||||
} from "../../../dialogs/config-flow/show-dialog-config-flow";
|
} from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||||
import { localizeConfigFlowTitle } from "../../../data/config_entries";
|
import { localizeConfigFlowTitle } from "../../../data/config_flow";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
@ -113,7 +113,7 @@ class HaConfigManagerDashboard extends LocalizeMixin(
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template is="dom-repeat" items="[[entries]]">
|
<template is="dom-repeat" items="[[entries]]">
|
||||||
<a href="/config/integrations/[[item.entry_id]]">
|
<a href="/config/integrations/config_entry/[[item.entry_id]]">
|
||||||
<paper-item>
|
<paper-item>
|
||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
<div>
|
<div>
|
||||||
@ -176,8 +176,6 @@ class HaConfigManagerDashboard extends LocalizeMixin(
|
|||||||
*/
|
*/
|
||||||
progress: Array,
|
progress: Array,
|
||||||
|
|
||||||
handlers: Array,
|
|
||||||
|
|
||||||
rtl: {
|
rtl: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
reflectToAttribute: true,
|
reflectToAttribute: true,
|
||||||
|
@ -1,184 +0,0 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import "../../../layouts/hass-subpage";
|
|
||||||
|
|
||||||
import "../../../components/entity/state-badge";
|
|
||||||
import { compare } from "../../../common/string/compare";
|
|
||||||
|
|
||||||
import "./ha-device-card";
|
|
||||||
import "./ha-ce-entities-card";
|
|
||||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
|
||||||
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
|
||||||
|
|
||||||
class HaConfigEntryPage extends NavigateMixin(
|
|
||||||
EventsMixin(LocalizeMixin(PolymerElement))
|
|
||||||
) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 4px;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex: 1 0 300px;
|
|
||||||
min-width: 0;
|
|
||||||
max-width: 500px;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<hass-subpage header="[[configEntry.title]]">
|
|
||||||
<template is="dom-if" if="[[configEntry.supports_options]]">
|
|
||||||
<paper-icon-button
|
|
||||||
slot="toolbar-icon"
|
|
||||||
icon="hass:settings"
|
|
||||||
on-click="_showSettings"
|
|
||||||
></paper-icon-button>
|
|
||||||
</template>
|
|
||||||
<paper-icon-button
|
|
||||||
slot="toolbar-icon"
|
|
||||||
icon="hass:delete"
|
|
||||||
on-click="_removeEntry"
|
|
||||||
></paper-icon-button>
|
|
||||||
<div class="content">
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if="[[_computeIsEmpty(_configEntryDevices, _noDeviceEntities)]]"
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
[[localize('ui.panel.config.integrations.config_entry.no_devices')]]
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template is="dom-repeat" items="[[_configEntryDevices]]" as="device">
|
|
||||||
<ha-device-card
|
|
||||||
class="card"
|
|
||||||
hass="[[hass]]"
|
|
||||||
areas="[[areas]]"
|
|
||||||
devices="[[devices]]"
|
|
||||||
device="[[device]]"
|
|
||||||
entities="[[entities]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
></ha-device-card>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[_noDeviceEntities.length]]">
|
|
||||||
<ha-ce-entities-card
|
|
||||||
class="card"
|
|
||||||
heading="[[localize('ui.panel.config.integrations.config_entry.no_device')]]"
|
|
||||||
entities="[[_noDeviceEntities]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
></ha-ce-entities-card>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
isWide: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
configEntry: {
|
|
||||||
type: Object,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
_configEntryDevices: {
|
|
||||||
type: Array,
|
|
||||||
computed: "_computeConfigEntryDevices(configEntry, devices)",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All entity registry entries for this config entry that do not belong
|
|
||||||
* to a device.
|
|
||||||
*/
|
|
||||||
_noDeviceEntities: {
|
|
||||||
type: Array,
|
|
||||||
computed: "_computeNoDeviceEntities(configEntry, entities)",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Area registry entries
|
|
||||||
*/
|
|
||||||
areas: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Device registry entries
|
|
||||||
*/
|
|
||||||
devices: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Existing entries.
|
|
||||||
*/
|
|
||||||
entries: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entity Registry entries.
|
|
||||||
*/
|
|
||||||
entities: Array,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeConfigEntryDevices(configEntry, devices) {
|
|
||||||
if (!devices) return [];
|
|
||||||
return devices
|
|
||||||
.filter((device) => device.config_entries.includes(configEntry.entry_id))
|
|
||||||
.sort(
|
|
||||||
(dev1, dev2) =>
|
|
||||||
!!dev1.via_device_id - !!dev2.via_device_id ||
|
|
||||||
compare(dev1.name, dev2.name)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeNoDeviceEntities(configEntry, entities) {
|
|
||||||
if (!entities) return [];
|
|
||||||
return entities.filter(
|
|
||||||
(ent) => !ent.device_id && ent.config_entry_id === configEntry.entry_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeIsEmpty(configEntryDevices, noDeviceEntities) {
|
|
||||||
return configEntryDevices.length === 0 && noDeviceEntities.length === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_showSettings() {
|
|
||||||
showOptionsFlowDialog(this, this.configEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeEntry() {
|
|
||||||
if (
|
|
||||||
!confirm(
|
|
||||||
this.localize(
|
|
||||||
"ui.panel.config.integrations.config_entry.delete_confirm"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const entryId = this.configEntry.entry_id;
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("delete", `config/config_entries/entry/${entryId}`)
|
|
||||||
.then((result) => {
|
|
||||||
this.fire("hass-reload-entries");
|
|
||||||
if (result.require_restart) {
|
|
||||||
alert(
|
|
||||||
this.localize(
|
|
||||||
"ui.panel.config.integrations.config_entry.restart_confirm"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.navigate("/config/integrations/dashboard", true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-config-entry-page", HaConfigEntryPage);
|
|
189
src/panels/config/integrations/ha-config-entry-page.ts
Normal file
189
src/panels/config/integrations/ha-config-entry-page.ts
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import "../../../layouts/hass-subpage";
|
||||||
|
import "../../../layouts/hass-error-screen";
|
||||||
|
|
||||||
|
import "../../../components/entity/state-badge";
|
||||||
|
import { compare } from "../../../common/string/compare";
|
||||||
|
|
||||||
|
import "./ha-device-card";
|
||||||
|
import "./ha-ce-entities-card";
|
||||||
|
import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow";
|
||||||
|
import { property, LitElement, CSSResult, css, html } from "lit-element";
|
||||||
|
import { navigate } from "../../../common/navigate";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { ConfigEntry, deleteConfigEntry } from "../../../data/config_entries";
|
||||||
|
import { EntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
|
import { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||||
|
import { AreaRegistryEntry } from "../../../data/area_registry";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
class HaConfigEntryPage extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
@property() public configEntryId!: string;
|
||||||
|
@property() public configEntries!: ConfigEntry[];
|
||||||
|
@property() public entityRegistryEntries!: EntityRegistryEntry[];
|
||||||
|
@property() public deviceRegistryEntries!: DeviceRegistryEntry[];
|
||||||
|
@property() public areas!: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
private get _configEntry(): ConfigEntry | undefined {
|
||||||
|
return this.configEntries
|
||||||
|
? this.configEntries.find(
|
||||||
|
(entry) => entry.entry_id === this.configEntryId
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeConfigEntryDevices = memoizeOne(
|
||||||
|
(configEntry: ConfigEntry, devices: DeviceRegistryEntry[]) => {
|
||||||
|
if (!devices) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return devices
|
||||||
|
.filter((device) =>
|
||||||
|
device.config_entries.includes(configEntry.entry_id)
|
||||||
|
)
|
||||||
|
.sort(
|
||||||
|
(dev1, dev2) =>
|
||||||
|
Number(!!dev1.via_device_id) - Number(!!dev2.via_device_id) ||
|
||||||
|
compare(dev1.name || "", dev2.name || "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _computeNoDeviceEntities = memoizeOne(
|
||||||
|
(configEntry: ConfigEntry, entities: EntityRegistryEntry[]) => {
|
||||||
|
if (!entities) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return entities.filter(
|
||||||
|
(ent) => !ent.device_id && ent.config_entry_id === configEntry.entry_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const configEntry = this._configEntry;
|
||||||
|
|
||||||
|
if (!configEntry) {
|
||||||
|
return html`
|
||||||
|
<hass-error-screen error="Integration not found."></hass-error-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const configEntryDevices = this._computeConfigEntryDevices(
|
||||||
|
configEntry,
|
||||||
|
this.deviceRegistryEntries
|
||||||
|
);
|
||||||
|
|
||||||
|
const noDeviceEntities = this._computeNoDeviceEntities(
|
||||||
|
configEntry,
|
||||||
|
this.entityRegistryEntries
|
||||||
|
);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-subpage .header=${configEntry.title}>
|
||||||
|
${configEntry.supports_options
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
slot="toolbar-icon"
|
||||||
|
icon="hass:settings"
|
||||||
|
@click=${this._showSettings}
|
||||||
|
></paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<paper-icon-button
|
||||||
|
slot="toolbar-icon"
|
||||||
|
icon="hass:delete"
|
||||||
|
@click=${this._removeEntry}
|
||||||
|
></paper-icon-button>
|
||||||
|
<div class="content">
|
||||||
|
${configEntryDevices.length === 0 && noDeviceEntities.length === 0
|
||||||
|
? html`
|
||||||
|
<p>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.no_devices"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${configEntryDevices.map(
|
||||||
|
(device) => html`
|
||||||
|
<ha-device-card
|
||||||
|
class="card"
|
||||||
|
.hass=${this.hass}
|
||||||
|
.areas=${this.areas}
|
||||||
|
.devices=${this.deviceRegistryEntries}
|
||||||
|
.device=${device}
|
||||||
|
.entities=${this.entityRegistryEntries}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-device-card>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
${noDeviceEntities.length > 0
|
||||||
|
? html`
|
||||||
|
<ha-ce-entities-card
|
||||||
|
class="card"
|
||||||
|
.heading=${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.no_device"
|
||||||
|
)}
|
||||||
|
.entities=${noDeviceEntities}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
></ha-ce-entities-card>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showSettings() {
|
||||||
|
showOptionsFlowDialog(this, this._configEntry!);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeEntry() {
|
||||||
|
if (
|
||||||
|
!confirm(
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.delete_confirm"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteConfigEntry(this.hass, this.configEntryId).then((result) => {
|
||||||
|
fireEvent(this, "hass-reload-entries");
|
||||||
|
if (result.require_restart) {
|
||||||
|
alert(
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.restart_confirm"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
navigate(this, "/config/integrations/dashboard", true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 300px;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 500px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-config-entry-page", HaConfigEntryPage);
|
@ -1,161 +0,0 @@
|
|||||||
import "@polymer/app-route/app-route";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
|
||||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
|
||||||
|
|
||||||
import "./ha-config-entries-dashboard";
|
|
||||||
import "./ha-config-entry-page";
|
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
|
||||||
import { compare } from "../../../common/string/compare";
|
|
||||||
import { subscribeAreaRegistry } from "../../../data/area_registry";
|
|
||||||
|
|
||||||
class HaConfigIntegrations extends NavigateMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<app-route
|
|
||||||
route="[[route]]"
|
|
||||||
pattern="/:page"
|
|
||||||
data="{{_routeData}}"
|
|
||||||
tail="{{_routeTail}}"
|
|
||||||
></app-route>
|
|
||||||
|
|
||||||
<template is="dom-if" if="[[_configEntry]]">
|
|
||||||
<ha-config-entry-page
|
|
||||||
hass="[[hass]]"
|
|
||||||
config-entry="[[_configEntry]]"
|
|
||||||
areas="[[_areas]]"
|
|
||||||
entries="[[_entries]]"
|
|
||||||
entities="[[_entities]]"
|
|
||||||
devices="[[_devices]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
></ha-config-entry-page>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[!_configEntry]]">
|
|
||||||
<ha-config-entries-dashboard
|
|
||||||
hass="[[hass]]"
|
|
||||||
entries="[[_entries]]"
|
|
||||||
entities="[[_entities]]"
|
|
||||||
handlers="[[_handlers]]"
|
|
||||||
progress="[[_progress]]"
|
|
||||||
></ha-config-entries-dashboard>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
isWide: Boolean,
|
|
||||||
narrow: Boolean,
|
|
||||||
route: Object,
|
|
||||||
|
|
||||||
_configEntry: {
|
|
||||||
type: Object,
|
|
||||||
computed: "_computeConfigEntry(_routeData, _entries)",
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Existing entries.
|
|
||||||
*/
|
|
||||||
_entries: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entity Registry entries.
|
|
||||||
*/
|
|
||||||
_entities: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Device Registry entries.
|
|
||||||
*/
|
|
||||||
_devices: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Area Registry entries.
|
|
||||||
*/
|
|
||||||
_areas: Array,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current flows that are in progress and have not been started by a user.
|
|
||||||
* For example, can be discovered devices that require more config.
|
|
||||||
*/
|
|
||||||
_progress: Array,
|
|
||||||
|
|
||||||
_handlers: Array,
|
|
||||||
|
|
||||||
_routeData: Object,
|
|
||||||
_routeTail: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
this.addEventListener("hass-reload-entries", () => this._loadData());
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this._loadData();
|
|
||||||
this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => {
|
|
||||||
this._areas = areas;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hass.connection
|
|
||||||
.subscribeEvents(() => {
|
|
||||||
this._debouncer = Debouncer.debounce(
|
|
||||||
this._debouncer,
|
|
||||||
timeOut.after(500),
|
|
||||||
() => this._loadData()
|
|
||||||
);
|
|
||||||
}, "config_entry_discovered")
|
|
||||||
.then((unsub) => {
|
|
||||||
this._unsubEvents = unsub;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
if (this._unsubEvents) this._unsubEvents();
|
|
||||||
if (this._unsubAreas) this._unsubAreas();
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadData() {
|
|
||||||
this.hass.callApi("get", "config/config_entries/entry").then((entries) => {
|
|
||||||
this._entries = entries.sort((conf1, conf2) =>
|
|
||||||
compare(conf1.title, conf2.title)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hass.callApi("get", "config/config_entries/flow").then((progress) => {
|
|
||||||
this._progress = progress;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callApi("get", "config/config_entries/flow_handlers")
|
|
||||||
.then((handlers) => {
|
|
||||||
this._handlers = handlers;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callWS({ type: "config/entity_registry/list" })
|
|
||||||
.then((entities) => {
|
|
||||||
this._entities = entities;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.hass
|
|
||||||
.callWS({ type: "config/device_registry/list" })
|
|
||||||
.then((devices) => {
|
|
||||||
this._devices = devices;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeConfigEntry(routeData, entries) {
|
|
||||||
return (
|
|
||||||
!!entries &&
|
|
||||||
!!routeData &&
|
|
||||||
entries.find((ent) => ent.entry_id === routeData.page)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-config-integrations", HaConfigIntegrations);
|
|
140
src/panels/config/integrations/ha-config-integrations.ts
Normal file
140
src/panels/config/integrations/ha-config-integrations.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import "@polymer/app-route/app-route";
|
||||||
|
|
||||||
|
import "./ha-config-entries-dashboard";
|
||||||
|
import "./ha-config-entry-page";
|
||||||
|
import { compare } from "../../../common/string/compare";
|
||||||
|
import {
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
AreaRegistryEntry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import {
|
||||||
|
HassRouterPage,
|
||||||
|
RouterOptions,
|
||||||
|
} from "../../../layouts/hass-router-page";
|
||||||
|
import { property, customElement, PropertyValues } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import { DataEntryFlowProgress } from "../../../data/data_entry_flow";
|
||||||
|
import { subscribeConfigFlowInProgress } from "../../../data/config_flow";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"hass-reload-entries": undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("ha-config-integrations")
|
||||||
|
class HaConfigIntegrations extends HassRouterPage {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
|
||||||
|
protected routerOptions: RouterOptions = {
|
||||||
|
defaultPage: "dashboard",
|
||||||
|
preloadAll: true,
|
||||||
|
routes: {
|
||||||
|
dashboard: {
|
||||||
|
tag: "ha-config-entries-dashboard",
|
||||||
|
},
|
||||||
|
config_entry: {
|
||||||
|
tag: "ha-config-entry-page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@property() private _configEntries?: ConfigEntry[];
|
||||||
|
@property() private _configEntriesInProgress?: DataEntryFlowProgress[];
|
||||||
|
@property() private _entityRegistryEntries?: EntityRegistryEntry[];
|
||||||
|
@property() private _deviceRegistryEntries?: DeviceRegistryEntry[];
|
||||||
|
@property() private _areas?: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
private _unsubs?: UnsubscribeFunc[];
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
if (!this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
if (this._unsubs) {
|
||||||
|
while (this._unsubs.length) {
|
||||||
|
this._unsubs.pop()!();
|
||||||
|
}
|
||||||
|
this._unsubs = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this.addEventListener("hass-reload-entries", () => this._loadData());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (!this._unsubs && changedProps.has("hass")) {
|
||||||
|
this._loadData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updatePageEl(pageEl) {
|
||||||
|
pageEl.hass = this.hass;
|
||||||
|
|
||||||
|
if (this._currentPage === "dashboard") {
|
||||||
|
pageEl.entities = this._entityRegistryEntries;
|
||||||
|
pageEl.entries = this._configEntries;
|
||||||
|
pageEl.progress = this._configEntriesInProgress;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageEl.entityRegistryEntries = this._entityRegistryEntries;
|
||||||
|
pageEl.configEntries = this._configEntries;
|
||||||
|
pageEl.configEntryId = this.routeTail.path.substr(1);
|
||||||
|
pageEl.deviceRegistryEntries = this._deviceRegistryEntries;
|
||||||
|
pageEl.areas = this._areas;
|
||||||
|
pageEl.narrow = this.narrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadData() {
|
||||||
|
getConfigEntries(this.hass).then((configEntries) => {
|
||||||
|
this._configEntries = configEntries.sort((conf1, conf2) =>
|
||||||
|
compare(conf1.title, conf2.title)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (this._unsubs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._unsubs = [
|
||||||
|
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
}),
|
||||||
|
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||||
|
this._entityRegistryEntries = entries;
|
||||||
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||||
|
this._deviceRegistryEntries = entries;
|
||||||
|
}),
|
||||||
|
subscribeConfigFlowInProgress(this.hass, (flowsInProgress) => {
|
||||||
|
this._configEntriesInProgress = flowsInProgress;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-integrations": HaConfigIntegrations;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user