mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-22 09:17:15 +00:00
Compare commits
7 Commits
add-automa
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be319503f7 | ||
|
|
9b1fe28018 | ||
|
|
0595f722f3 | ||
|
|
1c0315854a | ||
|
|
3b73d7c298 | ||
|
|
2955cb4956 | ||
|
|
c679e312a0 |
@@ -52,7 +52,7 @@
|
|||||||
"@fullcalendar/list": "6.1.19",
|
"@fullcalendar/list": "6.1.19",
|
||||||
"@fullcalendar/luxon3": "6.1.19",
|
"@fullcalendar/luxon3": "6.1.19",
|
||||||
"@fullcalendar/timegrid": "6.1.19",
|
"@fullcalendar/timegrid": "6.1.19",
|
||||||
"@home-assistant/webawesome": "3.0.0-ha.0",
|
"@home-assistant/webawesome": "3.0.0",
|
||||||
"@lezer/highlight": "1.2.3",
|
"@lezer/highlight": "1.2.3",
|
||||||
"@lit-labs/motion": "1.0.9",
|
"@lit-labs/motion": "1.0.9",
|
||||||
"@lit-labs/observers": "2.0.6",
|
"@lit-labs/observers": "2.0.6",
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ export class HaAuthFlow extends LitElement {
|
|||||||
willUpdate(changedProps: PropertyValues) {
|
willUpdate(changedProps: PropertyValues) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
|
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated && this.clientId === genClientId()) {
|
||||||
|
// Preselect store token when logging in to own instance
|
||||||
this._storeToken = this.initStoreToken;
|
this._storeToken = this.initStoreToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -597,10 +597,15 @@ export class HaChartBase extends LitElement {
|
|||||||
aria: { show: true },
|
aria: { show: true },
|
||||||
dataZoom: this._getDataZoomConfig(),
|
dataZoom: this._getDataZoomConfig(),
|
||||||
toolbox: {
|
toolbox: {
|
||||||
top: Infinity,
|
top: Number.MAX_SAFE_INTEGER,
|
||||||
left: Infinity,
|
left: Number.MAX_SAFE_INTEGER,
|
||||||
feature: {
|
feature: {
|
||||||
dataZoom: { show: true, yAxisIndex: false, filterMode: "none" },
|
dataZoom: {
|
||||||
|
show: true,
|
||||||
|
yAxisIndex: false,
|
||||||
|
filterMode: "none",
|
||||||
|
showTitle: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
iconStyle: { opacity: 0 },
|
iconStyle: { opacity: 0 },
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -154,10 +154,7 @@ export class HaLabelPicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this._getLabelsMemoized(
|
return this._getLabelsMemoized(
|
||||||
this.hass.states,
|
this.hass,
|
||||||
this.hass.areas,
|
|
||||||
this.hass.devices,
|
|
||||||
this.hass.entities,
|
|
||||||
this._labels,
|
this._labels,
|
||||||
this.includeDomains,
|
this.includeDomains,
|
||||||
this.excludeDomains,
|
this.excludeDomains,
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { css, html, LitElement } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
|
|
||||||
@customElement("ha-section-title")
|
|
||||||
class HaSectionTitle extends LitElement {
|
|
||||||
protected render() {
|
|
||||||
return html`<slot></slot>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
background-color: var(--ha-color-fill-neutral-quiet-resting);
|
|
||||||
padding: var(--ha-space-1) var(--ha-space-2);
|
|
||||||
font-weight: var(--ha-font-weight-bold);
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
min-height: var(--ha-space-6);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"ha-section-title": HaSectionTitle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,7 +30,6 @@ import {
|
|||||||
areaMeetsFilter,
|
areaMeetsFilter,
|
||||||
deviceMeetsFilter,
|
deviceMeetsFilter,
|
||||||
entityRegMeetsFilter,
|
entityRegMeetsFilter,
|
||||||
getTargetComboBoxItemType,
|
|
||||||
type TargetType,
|
type TargetType,
|
||||||
type TargetTypeFloorless,
|
type TargetTypeFloorless,
|
||||||
} from "../data/target";
|
} from "../data/target";
|
||||||
@@ -48,6 +47,7 @@ import "./ha-tree-indicator";
|
|||||||
import "./target-picker/ha-target-picker-item-group";
|
import "./target-picker/ha-target-picker-item-group";
|
||||||
import "./target-picker/ha-target-picker-value-chip";
|
import "./target-picker/ha-target-picker-value-chip";
|
||||||
|
|
||||||
|
const EMPTY_SEARCH = "___EMPTY_SEARCH___";
|
||||||
const SEPARATOR = "________";
|
const SEPARATOR = "________";
|
||||||
const CREATE_ID = "___create-new-entity___";
|
const CREATE_ID = "___create-new-entity___";
|
||||||
|
|
||||||
@@ -634,6 +634,35 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getRowType = (
|
||||||
|
item:
|
||||||
|
| PickerComboBoxItem
|
||||||
|
| (FloorComboBoxItem & { last?: boolean | undefined })
|
||||||
|
| EntityComboBoxItem
|
||||||
|
| DevicePickerItem
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
(item as FloorComboBoxItem).type === "area" ||
|
||||||
|
(item as FloorComboBoxItem).type === "floor"
|
||||||
|
) {
|
||||||
|
return (item as FloorComboBoxItem).type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("domain" in item) {
|
||||||
|
return "device";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("stateObj" in item) {
|
||||||
|
return "entity";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.id === EMPTY_SEARCH) {
|
||||||
|
return "empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "label";
|
||||||
|
};
|
||||||
|
|
||||||
private _sectionTitleFunction = ({
|
private _sectionTitleFunction = ({
|
||||||
firstIndex,
|
firstIndex,
|
||||||
lastIndex,
|
lastIndex,
|
||||||
@@ -657,7 +686,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = getTargetComboBoxItemType(firstItem as PickerComboBoxItem);
|
const type = this._getRowType(firstItem as PickerComboBoxItem);
|
||||||
const translationType:
|
const translationType:
|
||||||
| "areas"
|
| "areas"
|
||||||
| "entities"
|
| "entities"
|
||||||
@@ -829,10 +858,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
if (!filterType || filterType === "label") {
|
if (!filterType || filterType === "label") {
|
||||||
let labels = this._getLabelsMemoized(
|
let labels = this._getLabelsMemoized(
|
||||||
this.hass.states,
|
this.hass,
|
||||||
this.hass.areas,
|
|
||||||
this.hass.devices,
|
|
||||||
this.hass.entities,
|
|
||||||
this._labelRegistry,
|
this._labelRegistry,
|
||||||
includeDomains,
|
includeDomains,
|
||||||
undefined,
|
undefined,
|
||||||
@@ -948,7 +974,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = getTargetComboBoxItemType(item);
|
const type = this._getRowType(item);
|
||||||
let hasFloor = false;
|
let hasFloor = false;
|
||||||
let rtl = false;
|
let rtl = false;
|
||||||
let showEntityId = false;
|
let showEntityId = false;
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export class HaWaDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:host([width="large"]) wa-dialog {
|
:host([width="large"]) wa-dialog {
|
||||||
--width: min(var(--ha-dialog-width-lg, 1024px), var(--full-width));
|
--width: min(var(--ha-dialog-width-lg, 720px), var(--full-width));
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([width="full"]) wa-dialog {
|
:host([width="full"]) wa-dialog {
|
||||||
|
|||||||
@@ -21,52 +21,11 @@ export interface FloorComboBoxItem extends PickerComboBoxItem {
|
|||||||
area?: AreaRegistryEntry;
|
area?: AreaRegistryEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FloorNestedComboBoxItem extends PickerComboBoxItem {
|
|
||||||
floor?: FloorRegistryEntry;
|
|
||||||
areas: FloorComboBoxItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnassignedAreasFloorComboBoxItem extends PickerComboBoxItem {
|
|
||||||
areas: FloorComboBoxItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AreaFloorValue {
|
export interface AreaFloorValue {
|
||||||
id: string;
|
id: string;
|
||||||
type: "floor" | "area";
|
type: "floor" | "area";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAreasNestedInFloors = (
|
|
||||||
states: HomeAssistant["states"],
|
|
||||||
haFloors: HomeAssistant["floors"],
|
|
||||||
haAreas: HomeAssistant["areas"],
|
|
||||||
haDevices: HomeAssistant["devices"],
|
|
||||||
haEntities: HomeAssistant["entities"],
|
|
||||||
formatId: (value: AreaFloorValue) => string,
|
|
||||||
includeDomains?: string[],
|
|
||||||
excludeDomains?: string[],
|
|
||||||
includeDeviceClasses?: string[],
|
|
||||||
deviceFilter?: HaDevicePickerDeviceFilterFunc,
|
|
||||||
entityFilter?: HaEntityPickerEntityFilterFunc,
|
|
||||||
excludeAreas?: string[],
|
|
||||||
excludeFloors?: string[]
|
|
||||||
) =>
|
|
||||||
getAreasAndFloorsItems(
|
|
||||||
states,
|
|
||||||
haFloors,
|
|
||||||
haAreas,
|
|
||||||
haDevices,
|
|
||||||
haEntities,
|
|
||||||
formatId,
|
|
||||||
includeDomains,
|
|
||||||
excludeDomains,
|
|
||||||
includeDeviceClasses,
|
|
||||||
deviceFilter,
|
|
||||||
entityFilter,
|
|
||||||
excludeAreas,
|
|
||||||
excludeFloors,
|
|
||||||
true
|
|
||||||
) as (FloorNestedComboBoxItem | UnassignedAreasFloorComboBoxItem)[];
|
|
||||||
|
|
||||||
export const getAreasAndFloors = (
|
export const getAreasAndFloors = (
|
||||||
states: HomeAssistant["states"],
|
states: HomeAssistant["states"],
|
||||||
haFloors: HomeAssistant["floors"],
|
haFloors: HomeAssistant["floors"],
|
||||||
@@ -81,43 +40,7 @@ export const getAreasAndFloors = (
|
|||||||
entityFilter?: HaEntityPickerEntityFilterFunc,
|
entityFilter?: HaEntityPickerEntityFilterFunc,
|
||||||
excludeAreas?: string[],
|
excludeAreas?: string[],
|
||||||
excludeFloors?: string[]
|
excludeFloors?: string[]
|
||||||
) =>
|
): FloorComboBoxItem[] => {
|
||||||
getAreasAndFloorsItems(
|
|
||||||
states,
|
|
||||||
haFloors,
|
|
||||||
haAreas,
|
|
||||||
haDevices,
|
|
||||||
haEntities,
|
|
||||||
formatId,
|
|
||||||
includeDomains,
|
|
||||||
excludeDomains,
|
|
||||||
includeDeviceClasses,
|
|
||||||
deviceFilter,
|
|
||||||
entityFilter,
|
|
||||||
excludeAreas,
|
|
||||||
excludeFloors
|
|
||||||
) as FloorComboBoxItem[];
|
|
||||||
|
|
||||||
export const getAreasAndFloorsItems = (
|
|
||||||
states: HomeAssistant["states"],
|
|
||||||
haFloors: HomeAssistant["floors"],
|
|
||||||
haAreas: HomeAssistant["areas"],
|
|
||||||
haDevices: HomeAssistant["devices"],
|
|
||||||
haEntities: HomeAssistant["entities"],
|
|
||||||
formatId: (value: AreaFloorValue) => string,
|
|
||||||
includeDomains?: string[],
|
|
||||||
excludeDomains?: string[],
|
|
||||||
includeDeviceClasses?: string[],
|
|
||||||
deviceFilter?: HaDevicePickerDeviceFilterFunc,
|
|
||||||
entityFilter?: HaEntityPickerEntityFilterFunc,
|
|
||||||
excludeAreas?: string[],
|
|
||||||
excludeFloors?: string[],
|
|
||||||
nested = false
|
|
||||||
): (
|
|
||||||
| FloorComboBoxItem
|
|
||||||
| FloorNestedComboBoxItem
|
|
||||||
| UnassignedAreasFloorComboBoxItem
|
|
||||||
)[] => {
|
|
||||||
const floors = Object.values(haFloors);
|
const floors = Object.values(haFloors);
|
||||||
const areas = Object.values(haAreas);
|
const areas = Object.values(haAreas);
|
||||||
const devices = Object.values(haDevices);
|
const devices = Object.values(haDevices);
|
||||||
@@ -258,11 +181,7 @@ export const getAreasAndFloorsItems = (
|
|||||||
|
|
||||||
const hierarchy = getAreasFloorHierarchy(floors, outputAreas);
|
const hierarchy = getAreasFloorHierarchy(floors, outputAreas);
|
||||||
|
|
||||||
const items: (
|
const items: FloorComboBoxItem[] = [];
|
||||||
| FloorComboBoxItem
|
|
||||||
| FloorNestedComboBoxItem
|
|
||||||
| UnassignedAreasFloorComboBoxItem
|
|
||||||
)[] = [];
|
|
||||||
|
|
||||||
hierarchy.floors.forEach((f) => {
|
hierarchy.floors.forEach((f) => {
|
||||||
const floor = haFloors[f.id];
|
const floor = haFloors[f.id];
|
||||||
@@ -277,7 +196,7 @@ export const getAreasAndFloorsItems = (
|
|||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
const floorItem: FloorComboBoxItem | FloorNestedComboBoxItem = {
|
items.push({
|
||||||
id: formatId({ id: floor.floor_id, type: "floor" }),
|
id: formatId({ id: floor.floor_id, type: "floor" }),
|
||||||
type: "floor",
|
type: "floor",
|
||||||
primary: floorName,
|
primary: floorName,
|
||||||
@@ -289,11 +208,10 @@ export const getAreasAndFloorsItems = (
|
|||||||
...floor.aliases,
|
...floor.aliases,
|
||||||
...areaSearchLabels,
|
...areaSearchLabels,
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
|
|
||||||
items.push(floorItem);
|
items.push(
|
||||||
|
...floorAreas.map((area) => {
|
||||||
const floorAreasItems = floorAreas.map((area) => {
|
|
||||||
const areaName = computeAreaName(area);
|
const areaName = computeAreaName(area);
|
||||||
return {
|
return {
|
||||||
id: formatId({ id: area.area_id, type: "area" }),
|
id: formatId({ id: area.area_id, type: "area" }),
|
||||||
@@ -307,16 +225,12 @@ export const getAreasAndFloorsItems = (
|
|||||||
...area.aliases,
|
...area.aliases,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nested && floor) {
|
items.push(
|
||||||
(floorItem as unknown as FloorNestedComboBoxItem).areas = floorAreasItems;
|
...hierarchy.areas.map((areaId) => {
|
||||||
} else {
|
|
||||||
items.push(...floorAreasItems);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const unassignedAreaItems = hierarchy.areas.map((areaId) => {
|
|
||||||
const area = haAreas[areaId];
|
const area = haAreas[areaId];
|
||||||
const areaName = computeAreaName(area) || area.area_id;
|
const areaName = computeAreaName(area) || area.area_id;
|
||||||
return {
|
return {
|
||||||
@@ -327,15 +241,8 @@ export const getAreasAndFloorsItems = (
|
|||||||
icon: area.icon || undefined,
|
icon: area.icon || undefined,
|
||||||
search_labels: [area.area_id, areaName, ...area.aliases],
|
search_labels: [area.area_id, areaName, ...area.aliases],
|
||||||
};
|
};
|
||||||
});
|
})
|
||||||
|
);
|
||||||
if (nested && unassignedAreaItems.length) {
|
|
||||||
items.push({
|
|
||||||
areas: unassignedAreaItems,
|
|
||||||
} as UnassignedAreasFloorComboBoxItem);
|
|
||||||
} else {
|
|
||||||
items.push(...unassignedAreaItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import { stringCompare } from "../common/string/compare";
|
import { stringCompare } from "../common/string/compare";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { DeviceRegistryEntry } from "./device_registry";
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
import type {
|
import type { EntityRegistryEntry } from "./entity_registry";
|
||||||
EntityRegistryDisplayEntry,
|
|
||||||
EntityRegistryEntry,
|
|
||||||
} from "./entity_registry";
|
|
||||||
import type { RegistryEntry } from "./registry";
|
import type { RegistryEntry } from "./registry";
|
||||||
|
|
||||||
export { subscribeAreaRegistry } from "./ws-area_registry";
|
export { subscribeAreaRegistry } from "./ws-area_registry";
|
||||||
@@ -21,10 +18,7 @@ export interface AreaRegistryEntry extends RegistryEntry {
|
|||||||
temperature_entity_id: string | null;
|
temperature_entity_id: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AreaEntityLookup = Record<
|
export type AreaEntityLookup = Record<string, EntityRegistryEntry[]>;
|
||||||
string,
|
|
||||||
(EntityRegistryEntry | EntityRegistryDisplayEntry)[]
|
|
||||||
>;
|
|
||||||
|
|
||||||
export type AreaDeviceLookup = Record<string, DeviceRegistryEntry[]>;
|
export type AreaDeviceLookup = Record<string, DeviceRegistryEntry[]>;
|
||||||
|
|
||||||
@@ -75,17 +69,11 @@ export const reorderAreaRegistryEntries = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const getAreaEntityLookup = (
|
export const getAreaEntityLookup = (
|
||||||
entities: (EntityRegistryEntry | EntityRegistryDisplayEntry)[],
|
entities: EntityRegistryEntry[]
|
||||||
filterHidden = false
|
|
||||||
): AreaEntityLookup => {
|
): AreaEntityLookup => {
|
||||||
const areaEntityLookup: AreaEntityLookup = {};
|
const areaEntityLookup: AreaEntityLookup = {};
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (
|
if (!entity.area_id) {
|
||||||
!entity.area_id ||
|
|
||||||
(filterHidden &&
|
|
||||||
((entity as EntityRegistryDisplayEntry).hidden ||
|
|
||||||
(entity as EntityRegistryEntry).hidden_by))
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(entity.area_id in areaEntityLookup)) {
|
if (!(entity.area_id in areaEntityLookup)) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { formatTime } from "../common/datetime/format_time";
|
import { formatTime } from "../common/datetime/format_time";
|
||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
|
import { documentationUrl } from "../util/documentation-url";
|
||||||
import { fileDownload } from "../util/file_download";
|
import { fileDownload } from "../util/file_download";
|
||||||
import { handleFetchPromise } from "../util/hass-call-api";
|
import { handleFetchPromise } from "../util/hass-call-api";
|
||||||
import type { BackupManagerState, ManagerStateEvent } from "./backup_manager";
|
import type { BackupManagerState, ManagerStateEvent } from "./backup_manager";
|
||||||
@@ -414,7 +415,7 @@ ${hass.auth.data.hassUrl}
|
|||||||
${hass.localize("ui.panel.config.backup.emergency_kit_file.encryption_key")}
|
${hass.localize("ui.panel.config.backup.emergency_kit_file.encryption_key")}
|
||||||
${encryptionKey}
|
${encryptionKey}
|
||||||
|
|
||||||
${hass.localize("ui.panel.config.backup.emergency_kit_file.more_info", { link: "https://www.home-assistant.io/more-info/backup-emergency-kit" })}`);
|
${hass.localize("ui.panel.config.backup.emergency_kit_file.more_info", { link: documentationUrl(hass, "/more-info/backup-emergency-kit") })}`);
|
||||||
|
|
||||||
export const geneateEmergencyKitFileName = (
|
export const geneateEmergencyKitFileName = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
|||||||
@@ -50,10 +50,7 @@ export type DeviceEntityDisplayLookup = Record<
|
|||||||
EntityRegistryDisplayEntry[]
|
EntityRegistryDisplayEntry[]
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export type DeviceEntityLookup = Record<
|
export type DeviceEntityLookup = Record<string, EntityRegistryEntry[]>;
|
||||||
string,
|
|
||||||
(EntityRegistryEntry | EntityRegistryDisplayEntry)[]
|
|
||||||
>;
|
|
||||||
|
|
||||||
export interface DeviceRegistryEntryMutableParams {
|
export interface DeviceRegistryEntryMutableParams {
|
||||||
area_id?: string | null;
|
area_id?: string | null;
|
||||||
@@ -110,17 +107,11 @@ export const sortDeviceRegistryByName = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const getDeviceEntityLookup = (
|
export const getDeviceEntityLookup = (
|
||||||
entities: (EntityRegistryEntry | EntityRegistryDisplayEntry)[],
|
entities: EntityRegistryEntry[]
|
||||||
filterHidden = false
|
|
||||||
): DeviceEntityLookup => {
|
): DeviceEntityLookup => {
|
||||||
const deviceEntityLookup: DeviceEntityLookup = {};
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (
|
if (!entity.device_id) {
|
||||||
!entity.device_id ||
|
|
||||||
(filterHidden &&
|
|
||||||
((entity as EntityRegistryDisplayEntry).hidden ||
|
|
||||||
(entity as EntityRegistryEntry).hidden_by))
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(entity.device_id in deviceEntityLookup)) {
|
if (!(entity.device_id in deviceEntityLookup)) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { Connection } from "home-assistant-js-websocket";
|
|||||||
export interface CoreFrontendUserData {
|
export interface CoreFrontendUserData {
|
||||||
showAdvanced?: boolean;
|
showAdvanced?: boolean;
|
||||||
showEntityIdPicker?: boolean;
|
showEntityIdPicker?: boolean;
|
||||||
defaultPanel?: string;
|
default_panel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SidebarFrontendUserData {
|
export interface SidebarFrontendUserData {
|
||||||
@@ -12,7 +12,11 @@ export interface SidebarFrontendUserData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CoreFrontendSystemData {
|
export interface CoreFrontendSystemData {
|
||||||
defaultPanel?: string;
|
default_panel?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HomeFrontendSystemData {
|
||||||
|
favorite_entities?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@@ -22,6 +26,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
interface FrontendSystemData {
|
interface FrontendSystemData {
|
||||||
core: CoreFrontendSystemData;
|
core: CoreFrontendSystemData;
|
||||||
|
home: HomeFrontendSystemData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Connection } from "home-assistant-js-websocket";
|
import type { Connection } from "home-assistant-js-websocket";
|
||||||
import { createCollection } from "home-assistant-js-websocket";
|
import { createCollection } from "home-assistant-js-websocket";
|
||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import { debounce } from "../common/util/debounce";
|
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
|
import { debounce } from "../common/util/debounce";
|
||||||
|
|
||||||
export const integrationsWithPanel = {
|
export const integrationsWithPanel = {
|
||||||
bluetooth: "config/bluetooth",
|
bluetooth: "config/bluetooth",
|
||||||
@@ -25,8 +25,6 @@ export type IntegrationType =
|
|||||||
| "entity"
|
| "entity"
|
||||||
| "system";
|
| "system";
|
||||||
|
|
||||||
export type DomainManifestLookup = Record<string, IntegrationManifest>;
|
|
||||||
|
|
||||||
export interface IntegrationManifest {
|
export interface IntegrationManifest {
|
||||||
is_built_in: boolean;
|
is_built_in: boolean;
|
||||||
overwrites_built_in?: boolean;
|
overwrites_built_in?: boolean;
|
||||||
|
|||||||
@@ -101,10 +101,7 @@ export const deleteLabelRegistryEntry = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const getLabels = (
|
export const getLabels = (
|
||||||
hassStates: HomeAssistant["states"],
|
hass: HomeAssistant,
|
||||||
hassAreas: HomeAssistant["areas"],
|
|
||||||
hassDevices: HomeAssistant["devices"],
|
|
||||||
hassEntities: HomeAssistant["entities"],
|
|
||||||
labels?: LabelRegistryEntry[],
|
labels?: LabelRegistryEntry[],
|
||||||
includeDomains?: string[],
|
includeDomains?: string[],
|
||||||
excludeDomains?: string[],
|
excludeDomains?: string[],
|
||||||
@@ -118,8 +115,8 @@ export const getLabels = (
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const devices = Object.values(hassDevices);
|
const devices = Object.values(hass.devices);
|
||||||
const entities = Object.values(hassEntities);
|
const entities = Object.values(hass.entities);
|
||||||
|
|
||||||
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
||||||
let inputDevices: DeviceRegistryEntry[] | undefined;
|
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||||
@@ -173,7 +170,7 @@ export const getLabels = (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return deviceEntityLookup[device.id].some((entity) => {
|
return deviceEntityLookup[device.id].some((entity) => {
|
||||||
const stateObj = hassStates[entity.entity_id];
|
const stateObj = hass.states[entity.entity_id];
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -184,7 +181,7 @@ export const getLabels = (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
inputEntities = inputEntities!.filter((entity) => {
|
inputEntities = inputEntities!.filter((entity) => {
|
||||||
const stateObj = hassStates[entity.entity_id];
|
const stateObj = hass.states[entity.entity_id];
|
||||||
return (
|
return (
|
||||||
stateObj.attributes.device_class &&
|
stateObj.attributes.device_class &&
|
||||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||||
@@ -203,7 +200,7 @@ export const getLabels = (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return deviceEntityLookup[device.id].some((entity) => {
|
return deviceEntityLookup[device.id].some((entity) => {
|
||||||
const stateObj = hassStates[entity.entity_id];
|
const stateObj = hass.states[entity.entity_id];
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -211,7 +208,7 @@ export const getLabels = (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
inputEntities = inputEntities!.filter((entity) => {
|
inputEntities = inputEntities!.filter((entity) => {
|
||||||
const stateObj = hassStates[entity.entity_id];
|
const stateObj = hass.states[entity.entity_id];
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -248,7 +245,7 @@ export const getLabels = (
|
|||||||
|
|
||||||
if (areaIds) {
|
if (areaIds) {
|
||||||
areaIds.forEach((areaId) => {
|
areaIds.forEach((areaId) => {
|
||||||
const area = hassAreas[areaId];
|
const area = hass.areas[areaId];
|
||||||
area.labels.forEach((label) => usedLabels.add(label));
|
area.labels.forEach((label) => usedLabels.add(label));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export const getLegacyDefaultPanelUrlPath = (): string | null => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getDefaultPanelUrlPath = (hass: HomeAssistant): string =>
|
export const getDefaultPanelUrlPath = (hass: HomeAssistant): string =>
|
||||||
hass.userData?.defaultPanel ||
|
hass.userData?.default_panel ||
|
||||||
hass.systemData?.defaultPanel ||
|
hass.systemData?.default_panel ||
|
||||||
getLegacyDefaultPanelUrlPath() ||
|
getLegacyDefaultPanelUrlPath() ||
|
||||||
DEFAULT_PANEL;
|
DEFAULT_PANEL;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||||
import { computeDomain } from "../common/entity/compute_domain";
|
import { computeDomain } from "../common/entity/compute_domain";
|
||||||
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
|
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
|
||||||
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
|
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { FloorComboBoxItem } from "./area_floor";
|
|
||||||
import type { AreaRegistryEntry } from "./area_registry";
|
import type { AreaRegistryEntry } from "./area_registry";
|
||||||
import type { DevicePickerItem, DeviceRegistryEntry } from "./device_registry";
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
import type { HaEntityPickerEntityFilterFunc } from "./entity";
|
import type { HaEntityPickerEntityFilterFunc } from "./entity";
|
||||||
import type {
|
import type { EntityRegistryDisplayEntry } from "./entity_registry";
|
||||||
EntityComboBoxItem,
|
|
||||||
EntityRegistryDisplayEntry,
|
|
||||||
} from "./entity_registry";
|
|
||||||
|
|
||||||
export const TARGET_SEPARATOR = "________";
|
|
||||||
|
|
||||||
export type TargetType = "entity" | "device" | "area" | "label" | "floor";
|
export type TargetType = "entity" | "device" | "area" | "label" | "floor";
|
||||||
export type TargetTypeFloorless = Exclude<TargetType, "floor">;
|
export type TargetTypeFloorless = Exclude<TargetType, "floor">;
|
||||||
@@ -42,28 +35,6 @@ export const extractFromTarget = async (
|
|||||||
target,
|
target,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getTriggersForTarget = async (
|
|
||||||
callWS: HomeAssistant["callWS"],
|
|
||||||
target: HassServiceTarget,
|
|
||||||
expandGroup = true
|
|
||||||
) =>
|
|
||||||
callWS<string[]>({
|
|
||||||
type: "get_triggers_for_target",
|
|
||||||
target,
|
|
||||||
expand_group: expandGroup,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getServicesForTarget = async (
|
|
||||||
callWS: HomeAssistant["callWS"],
|
|
||||||
target: HassServiceTarget,
|
|
||||||
expandGroup = true
|
|
||||||
) =>
|
|
||||||
callWS<string[]>({
|
|
||||||
type: "get_services_for_target",
|
|
||||||
target,
|
|
||||||
expand_group: expandGroup,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const areaMeetsFilter = (
|
export const areaMeetsFilter = (
|
||||||
area: AreaRegistryEntry,
|
area: AreaRegistryEntry,
|
||||||
devices: HomeAssistant["devices"],
|
devices: HomeAssistant["devices"],
|
||||||
@@ -191,32 +162,3 @@ export const entityRegMeetsFilter = (
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTargetComboBoxItemType = (
|
|
||||||
item:
|
|
||||||
| PickerComboBoxItem
|
|
||||||
| (FloorComboBoxItem & { last?: boolean | undefined })
|
|
||||||
| EntityComboBoxItem
|
|
||||||
| DevicePickerItem
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
(item as FloorComboBoxItem).type === "area" ||
|
|
||||||
(item as FloorComboBoxItem).type === "floor"
|
|
||||||
) {
|
|
||||||
return (item as FloorComboBoxItem).type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("domain" in item) {
|
|
||||||
return "device";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("stateObj" in item) {
|
|
||||||
return "entity";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.id === "___EMPTY_SEARCH___") {
|
|
||||||
return "empty";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "label";
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { atLeastVersion } from "../common/config/version";
|
|||||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||||
import "../components/ha-card";
|
import "../components/ha-card";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { documentationUrl } from "../util/documentation-url";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./hass-subpage";
|
import "./hass-subpage";
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ class SupervisorErrorScreen extends LitElement {
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="https://www.home-assistant.io/help/"
|
href=${documentationUrl(this.hass, "/help/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import type { LocalizeFunc } from "../common/translations/localize";
|
import type { LocalizeFunc } from "../common/translations/localize";
|
||||||
import "../components/ha-card";
|
import "../components/ha-card";
|
||||||
|
import { documentationUrl } from "../util/documentation-url";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { showAppDialog } from "./dialogs/show-app-dialog";
|
import { showAppDialog } from "./dialogs/show-app-dialog";
|
||||||
import { showCommunityDialog } from "./dialogs/show-community-dialog";
|
import { showCommunityDialog } from "./dialogs/show-community-dialog";
|
||||||
@@ -22,7 +23,10 @@ class OnboardingWelcomeLinks extends LitElement {
|
|||||||
return html`<a
|
return html`<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
href="https://www.home-assistant.io/blog/2016/01/19/perfect-home-automation/"
|
href=${documentationUrl(
|
||||||
|
this.hass,
|
||||||
|
"/blog/2016/01/19/perfect-home-automation/"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<onboarding-welcome-link
|
<onboarding-welcome-link
|
||||||
noninteractive
|
noninteractive
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ import { mdiClose, mdiOpenInNew } from "@mdi/js";
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-code-editor";
|
import "../../../components/ha-code-editor";
|
||||||
@@ -140,7 +141,7 @@ class DialogImportBlueprint extends LitElement {
|
|||||||
<ha-button
|
<ha-button
|
||||||
size="small"
|
size="small"
|
||||||
appearance="plain"
|
appearance="plain"
|
||||||
href="https://www.home-assistant.io/get-blueprints"
|
href=${documentationUrl(this.hass, "/get-blueprints")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ class HaBlueprintOverview extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-button
|
<ha-button
|
||||||
appearance="plain"
|
appearance="plain"
|
||||||
href="https://www.home-assistant.io/get-blueprints"
|
href=${documentationUrl(this.hass, "/get-blueprints")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { css, html, LitElement, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "../../../../components/ha-alert";
|
import "../../../../components/ha-alert";
|
||||||
import type { EnergyValidationIssue } from "../../../../data/energy";
|
import type { EnergyValidationIssue } from "../../../../data/energy";
|
||||||
|
import { documentationUrl } from "../../../../util/documentation-url";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
@customElement("ha-energy-validation-result")
|
@customElement("ha-energy-validation-result")
|
||||||
@@ -29,7 +30,10 @@ class EnergyValidationMessage extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${issue.type === "recorder_untracked"
|
${issue.type === "recorder_untracked"
|
||||||
? html`(<a
|
? html`(<a
|
||||||
href="https://www.home-assistant.io/integrations/recorder#configure-filter"
|
href=${documentationUrl(
|
||||||
|
this.hass,
|
||||||
|
"/integrations/recorder#configure-filter"
|
||||||
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>${this.hass.localize("ui.panel.config.common.learn_more")}</a
|
>${this.hass.localize("ui.panel.config.common.learn_more")}</a
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js";
|
import {
|
||||||
|
mdiBookshelf,
|
||||||
|
mdiCog,
|
||||||
|
mdiDelete,
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiOpenInNew,
|
||||||
|
} from "@mdi/js";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
@@ -7,6 +13,11 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
|||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-button-menu";
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-list-item";
|
import "../../../components/ha-list-item";
|
||||||
|
import {
|
||||||
|
deleteApplicationCredential,
|
||||||
|
fetchApplicationCredentialsConfigEntry,
|
||||||
|
} from "../../../data/application_credential";
|
||||||
|
import { deleteConfigEntry } from "../../../data/config_entries";
|
||||||
import {
|
import {
|
||||||
ATTENTION_SOURCES,
|
ATTENTION_SOURCES,
|
||||||
DISCOVERY_SOURCES,
|
DISCOVERY_SOURCES,
|
||||||
@@ -15,7 +26,10 @@ import {
|
|||||||
} from "../../../data/config_flow";
|
} from "../../../data/config_flow";
|
||||||
import type { IntegrationManifest } from "../../../data/integration";
|
import type { IntegrationManifest } from "../../../data/integration";
|
||||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import {
|
||||||
|
showAlertDialog,
|
||||||
|
showConfirmationDialog,
|
||||||
|
} from "../../../dialogs/generic/show-dialog-box";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { documentationUrl } from "../../../util/documentation-url";
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import type { DataEntryFlowProgressExtended } from "./ha-config-integrations";
|
import type { DataEntryFlowProgressExtended } from "./ha-config-integrations";
|
||||||
@@ -60,7 +74,7 @@ export class HaConfigFlowCard extends LitElement {
|
|||||||
: "ui.common.add"
|
: "ui.common.add"
|
||||||
)}
|
)}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
${this.flow.context.configuration_url || this.manifest
|
${this.flow.context.configuration_url || this.manifest || attention
|
||||||
? html`<ha-button-menu slot="header-button">
|
? html`<ha-button-menu slot="header-button">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@@ -118,6 +132,22 @@ export class HaConfigFlowCard extends LitElement {
|
|||||||
</ha-list-item>
|
</ha-list-item>
|
||||||
</a>`
|
</a>`
|
||||||
: ""}
|
: ""}
|
||||||
|
${attention
|
||||||
|
? html`<ha-list-item
|
||||||
|
class="warning"
|
||||||
|
graphic="icon"
|
||||||
|
@click=${this._handleDelete}
|
||||||
|
>
|
||||||
|
<ha-svg-icon
|
||||||
|
class="warning"
|
||||||
|
slot="graphic"
|
||||||
|
.path=${mdiDelete}
|
||||||
|
></ha-svg-icon>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.delete"
|
||||||
|
)}
|
||||||
|
</ha-list-item>`
|
||||||
|
: ""}
|
||||||
</ha-button-menu>`
|
</ha-button-menu>`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-integration-action-card>
|
</ha-integration-action-card>
|
||||||
@@ -175,6 +205,109 @@ export class HaConfigFlowCard extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return an application credentials id for this config entry to prompt the
|
||||||
|
// user for removal. This is best effort so we don't stop overall removal
|
||||||
|
// if the integration isn't loaded or there is some other error.
|
||||||
|
private async _fetchApplicationCredentials(entryId: string) {
|
||||||
|
try {
|
||||||
|
return (await fetchApplicationCredentialsConfigEntry(this.hass, entryId))
|
||||||
|
.application_credentials_id;
|
||||||
|
} catch (_err: any) {
|
||||||
|
// We won't prompt the user to remove credentials
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _removeApplicationCredential(applicationCredentialsId: string) {
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.application_credentials.delete_title"
|
||||||
|
),
|
||||||
|
text: html`${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.application_credentials.delete_prompt"
|
||||||
|
)},
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.application_credentials.delete_detail"
|
||||||
|
)}
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<a
|
||||||
|
href="https://www.home-assistant.io/integrations/application_credentials"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.application_credentials.learn_more"
|
||||||
|
)}
|
||||||
|
</a>`,
|
||||||
|
confirmText: this.hass.localize("ui.common.delete"),
|
||||||
|
dismissText: this.hass.localize("ui.common.cancel"),
|
||||||
|
destructive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteApplicationCredential(this.hass, applicationCredentialsId);
|
||||||
|
} catch (err: any) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.application_credentials.delete_error_title"
|
||||||
|
),
|
||||||
|
text: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleDelete() {
|
||||||
|
const entryId = this.flow.context.entry_id;
|
||||||
|
|
||||||
|
if (!entryId) {
|
||||||
|
// This shouldn't happen for reauth flows, but handle gracefully
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const applicationCredentialsId =
|
||||||
|
await this._fetchApplicationCredentials(entryId);
|
||||||
|
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.delete_confirm_title",
|
||||||
|
{ title: localizeConfigFlowTitle(this.hass.localize, this.flow) }
|
||||||
|
),
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.delete_confirm_text"
|
||||||
|
),
|
||||||
|
confirmText: this.hass!.localize("ui.common.delete"),
|
||||||
|
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||||
|
destructive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await deleteConfigEntry(this.hass, entryId);
|
||||||
|
|
||||||
|
if (result.require_restart) {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.config_entry.restart_confirm"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applicationCredentialsId) {
|
||||||
|
this._removeApplicationCredential(applicationCredentialsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._handleFlowUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -191,6 +324,9 @@ export class HaConfigFlowCard extends LitElement {
|
|||||||
--mdc-theme-primary: var(--error-color);
|
--mdc-theme-primary: var(--error-color);
|
||||||
--ha-card-border-color: var(--error-color);
|
--ha-card-border-color: var(--error-color);
|
||||||
}
|
}
|
||||||
|
.warning {
|
||||||
|
--mdc-theme-text-primary-on-background: var(--error-color);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
|||||||
color:
|
color:
|
||||||
route.route_status === "Active"
|
route.route_status === "Active"
|
||||||
? primaryColor
|
? primaryColor
|
||||||
: style.getPropertyValue("--disabled-color"),
|
: style.getPropertyValue("--dark-primary-color"),
|
||||||
type: ["Child", "Parent"].includes(neighbor.relationship)
|
type: ["Child", "Parent"].includes(neighbor.relationship)
|
||||||
? "solid"
|
? "solid"
|
||||||
: "dotted",
|
: "dotted",
|
||||||
@@ -335,7 +335,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
|
|||||||
symbolSize: 5,
|
symbolSize: 5,
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 1,
|
width: 1,
|
||||||
color: style.getPropertyValue("--disabled-color"),
|
color: style.getPropertyValue("--dark-primary-color"),
|
||||||
type: "dotted",
|
type: "dotted",
|
||||||
},
|
},
|
||||||
ignoreForceLayout: true,
|
ignoreForceLayout: true,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { showLabsPreviewFeatureEnableDialog } from "./show-dialog-labs-preview-feature-enable";
|
import { showLabsPreviewFeatureEnableDialog } from "./show-dialog-labs-preview-feature-enable";
|
||||||
import {
|
import {
|
||||||
@@ -100,7 +101,7 @@ class HaConfigLabs extends SubscribeMixin(LitElement) {
|
|||||||
? html`
|
? html`
|
||||||
<a
|
<a
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
href="https://www.home-assistant.io/integrations/labs/"
|
href=${documentationUrl(this.hass, "/integrations/labs/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
.title=${this.hass.localize("ui.common.help")}
|
.title=${this.hass.localize("ui.common.help")}
|
||||||
@@ -124,7 +125,7 @@ class HaConfigLabs extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.labs.empty.description"
|
"ui.panel.config.labs.empty.description"
|
||||||
)}
|
)}
|
||||||
<a
|
<a
|
||||||
href="https://www.home-assistant.io/integrations/labs/"
|
href=${documentationUrl(this.hass, "/integrations/labs/")}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
const defaultPanelUrlPath =
|
const defaultPanelUrlPath =
|
||||||
this.hass.systemData?.defaultPanel || DEFAULT_PANEL;
|
this.hass.systemData?.default_panel || DEFAULT_PANEL;
|
||||||
const titleInvalid = !this._data.title || !this._data.title.trim();
|
const titleInvalid = !this._data.title || !this._data.title.trim();
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@@ -260,7 +260,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPanel = this.hass.systemData?.defaultPanel || DEFAULT_PANEL;
|
const defaultPanel = this.hass.systemData?.default_panel || DEFAULT_PANEL;
|
||||||
// Add warning dialog to saying that this will change the default dashboard for all users
|
// Add warning dialog to saying that this will change the default dashboard for all users
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@@ -284,7 +284,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
|||||||
|
|
||||||
saveFrontendSystemData(this.hass.connection, "core", {
|
saveFrontendSystemData(this.hass.connection, "core", {
|
||||||
...this.hass.systemData,
|
...this.hass.systemData,
|
||||||
defaultPanel: urlPath === defaultPanel ? undefined : urlPath,
|
default_panel: urlPath === defaultPanel ? undefined : urlPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPanel = this.hass.systemData?.defaultPanel || DEFAULT_PANEL;
|
const defaultPanel = this.hass.systemData?.default_panel || DEFAULT_PANEL;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { CSSResultGroup } from "lit";
|
|||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { documentationUrl } from "../../../util/documentation-url";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
@@ -14,8 +15,6 @@ import { haStyleDialog } from "../../../resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import type { TagDetailDialogParams } from "./show-dialog-tag-detail";
|
import type { TagDetailDialogParams } from "./show-dialog-tag-detail";
|
||||||
|
|
||||||
const TAG_BASE = "https://www.home-assistant.io/tag/";
|
|
||||||
|
|
||||||
@customElement("dialog-tag-detail")
|
@customElement("dialog-tag-detail")
|
||||||
class DialogTagDetail
|
class DialogTagDetail
|
||||||
extends LitElement
|
extends LitElement
|
||||||
@@ -122,7 +121,7 @@ class DialogTagDetail
|
|||||||
</div>
|
</div>
|
||||||
<div id="qr">
|
<div id="qr">
|
||||||
<ha-qr-code
|
<ha-qr-code
|
||||||
.data=${`${TAG_BASE}${this._params!.entry!.id}`}
|
.data=${`${documentationUrl(this.hass, "/tag/")}${this._params!.entry!.id}`}
|
||||||
center-image="/static/icons/favicon-192x192.png"
|
center-image="/static/icons/favicon-192x192.png"
|
||||||
error-correction-level="quartile"
|
error-correction-level="quartile"
|
||||||
scale="5"
|
scale="5"
|
||||||
|
|||||||
151
src/panels/home/dialogs/dialog-edit-home.ts
Normal file
151
src/panels/home/dialogs/dialog-edit-home.ts
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import "../../../components/entity/ha-entities-picker";
|
||||||
|
import "../../../components/ha-button";
|
||||||
|
import "../../../components/ha-dialog-footer";
|
||||||
|
import "../../../components/ha-wa-dialog";
|
||||||
|
import type { HomeFrontendSystemData } from "../../../data/frontend";
|
||||||
|
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||||
|
import { haStyleDialog } from "../../../resources/styles";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import type { EditHomeDialogParams } from "./show-dialog-edit-home";
|
||||||
|
|
||||||
|
@customElement("dialog-edit-home")
|
||||||
|
export class DialogEditHome
|
||||||
|
extends LitElement
|
||||||
|
implements HassDialog<EditHomeDialogParams>
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _params?: EditHomeDialogParams;
|
||||||
|
|
||||||
|
@state() private _config?: HomeFrontendSystemData;
|
||||||
|
|
||||||
|
@state() private _open = false;
|
||||||
|
|
||||||
|
@state() private _submitting = false;
|
||||||
|
|
||||||
|
public showDialog(params: EditHomeDialogParams): void {
|
||||||
|
this._params = params;
|
||||||
|
this._config = { ...params.config };
|
||||||
|
this._open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeDialog(): boolean {
|
||||||
|
this._open = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dialogClosed(): void {
|
||||||
|
this._params = undefined;
|
||||||
|
this._config = undefined;
|
||||||
|
this._submitting = false;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._params) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-wa-dialog
|
||||||
|
.hass=${this.hass}
|
||||||
|
.open=${this._open}
|
||||||
|
.headerTitle=${this.hass.localize("ui.panel.home.editor.title")}
|
||||||
|
@closed=${this._dialogClosed}
|
||||||
|
>
|
||||||
|
<p class="description">
|
||||||
|
${this.hass.localize("ui.panel.home.editor.description")}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ha-entities-picker
|
||||||
|
autofocus
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._config?.favorite_entities || []}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.strategy.home.favorite_entities"
|
||||||
|
)}
|
||||||
|
.placeholder=${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.editor.strategy.home.add_favorite_entity"
|
||||||
|
)}
|
||||||
|
.helper=${this.hass.localize(
|
||||||
|
"ui.panel.home.editor.favorite_entities_helper"
|
||||||
|
)}
|
||||||
|
reorder
|
||||||
|
allow-custom-entity
|
||||||
|
@value-changed=${this._favoriteEntitiesChanged}
|
||||||
|
></ha-entities-picker>
|
||||||
|
|
||||||
|
<ha-dialog-footer slot="footer">
|
||||||
|
<ha-button
|
||||||
|
appearance="plain"
|
||||||
|
slot="secondaryAction"
|
||||||
|
@click=${this.closeDialog}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.common.cancel")}
|
||||||
|
</ha-button>
|
||||||
|
<ha-button
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._save}
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
${this.hass.localize("ui.common.save")}
|
||||||
|
</ha-button>
|
||||||
|
</ha-dialog-footer>
|
||||||
|
</ha-wa-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _favoriteEntitiesChanged(ev: CustomEvent): void {
|
||||||
|
const entities = ev.detail.value as string[];
|
||||||
|
this._config = {
|
||||||
|
...this._config,
|
||||||
|
favorite_entities: entities.length > 0 ? entities : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _save(): Promise<void> {
|
||||||
|
if (!this._params || !this._config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._submitting = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._params.saveConfig(this._config);
|
||||||
|
this.closeDialog();
|
||||||
|
} catch (err: any) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Failed to save home configuration:", err);
|
||||||
|
} finally {
|
||||||
|
this._submitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
ha-wa-dialog {
|
||||||
|
--dialog-content-padding: var(--ha-space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 0 0 var(--ha-space-4) 0;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-entities-picker {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-edit-home": DialogEditHome;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/panels/home/dialogs/show-dialog-edit-home.ts
Normal file
20
src/panels/home/dialogs/show-dialog-edit-home.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import type { HomeFrontendSystemData } from "../../../data/frontend";
|
||||||
|
|
||||||
|
export interface EditHomeDialogParams {
|
||||||
|
config: HomeFrontendSystemData;
|
||||||
|
saveConfig: (config: HomeFrontendSystemData) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadEditHomeDialog = () => import("./dialog-edit-home");
|
||||||
|
|
||||||
|
export const showEditHomeDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
params: EditHomeDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-edit-home",
|
||||||
|
dialogImport: loadEditHomeDialog,
|
||||||
|
dialogParams: params,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -3,18 +3,18 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
import { deepEqual } from "../../common/util/deep-equal";
|
import { deepEqual } from "../../common/util/deep-equal";
|
||||||
|
import {
|
||||||
|
fetchFrontendSystemData,
|
||||||
|
saveFrontendSystemData,
|
||||||
|
type HomeFrontendSystemData,
|
||||||
|
} from "../../data/frontend";
|
||||||
import type { LovelaceDashboardStrategyConfig } from "../../data/lovelace/config/types";
|
import type { LovelaceDashboardStrategyConfig } from "../../data/lovelace/config/types";
|
||||||
import type { HomeAssistant, PanelInfo, Route } from "../../types";
|
import type { HomeAssistant, PanelInfo, Route } from "../../types";
|
||||||
|
import { showToast } from "../../util/toast";
|
||||||
import "../lovelace/hui-root";
|
import "../lovelace/hui-root";
|
||||||
import { generateLovelaceDashboardStrategy } from "../lovelace/strategies/get-strategy";
|
import { generateLovelaceDashboardStrategy } from "../lovelace/strategies/get-strategy";
|
||||||
import type { Lovelace } from "../lovelace/types";
|
import type { Lovelace } from "../lovelace/types";
|
||||||
import { showAlertDialog } from "../lovelace/custom-card-helpers";
|
import { showEditHomeDialog } from "./dialogs/show-dialog-edit-home";
|
||||||
|
|
||||||
const HOME_LOVELACE_CONFIG: LovelaceDashboardStrategyConfig = {
|
|
||||||
strategy: {
|
|
||||||
type: "home",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
@customElement("ha-panel-home")
|
@customElement("ha-panel-home")
|
||||||
class PanelHome extends LitElement {
|
class PanelHome extends LitElement {
|
||||||
@@ -28,12 +28,14 @@ class PanelHome extends LitElement {
|
|||||||
|
|
||||||
@state() private _lovelace?: Lovelace;
|
@state() private _lovelace?: Lovelace;
|
||||||
|
|
||||||
|
@state() private _config: FrontendSystemData["home"] = {};
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
super.willUpdate(changedProps);
|
super.willUpdate(changedProps);
|
||||||
// Initial setup
|
// Initial setup
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
this.hass.loadFragmentTranslation("lovelace");
|
this.hass.loadFragmentTranslation("lovelace");
|
||||||
this._setLovelace();
|
this._loadConfig();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,9 +97,28 @@ class PanelHome extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _loadConfig() {
|
||||||
|
try {
|
||||||
|
const data = await fetchFrontendSystemData(this.hass.connection, "home");
|
||||||
|
this._config = data || {};
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Failed to load favorites:", err);
|
||||||
|
this._config = {};
|
||||||
|
}
|
||||||
|
this._setLovelace();
|
||||||
|
}
|
||||||
|
|
||||||
private async _setLovelace() {
|
private async _setLovelace() {
|
||||||
|
const strategyConfig: LovelaceDashboardStrategyConfig = {
|
||||||
|
strategy: {
|
||||||
|
type: "home",
|
||||||
|
favorite_entities: this._config.favorite_entities,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const config = await generateLovelaceDashboardStrategy(
|
const config = await generateLovelaceDashboardStrategy(
|
||||||
HOME_LOVELACE_CONFIG,
|
strategyConfig,
|
||||||
this.hass
|
this.hass
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,15 +142,34 @@ class PanelHome extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _setEditMode = () => {
|
private _setEditMode = () => {
|
||||||
// For now, we just show an alert that edit mode is not supported.
|
showEditHomeDialog(this, {
|
||||||
// This will be expanded in the future.
|
config: this._config,
|
||||||
showAlertDialog(this, {
|
saveConfig: async (config) => {
|
||||||
title: "Edit mode not available",
|
await this._saveConfig(config);
|
||||||
text: "The Home panel does not support edit mode.",
|
},
|
||||||
confirmText: this.hass.localize("ui.common.ok"),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private async _saveConfig(config: HomeFrontendSystemData): Promise<void> {
|
||||||
|
try {
|
||||||
|
await saveFrontendSystemData(this.hass.connection, "home", config);
|
||||||
|
this._config = config || {};
|
||||||
|
} catch (err: any) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Failed to save home configuration:", err);
|
||||||
|
showToast(this, {
|
||||||
|
message: this.hass.localize("ui.panel.home.editor.save_failed"),
|
||||||
|
duration: 0,
|
||||||
|
dismissable: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showToast(this, {
|
||||||
|
message: this.hass.localize("ui.common.successfully_saved"),
|
||||||
|
});
|
||||||
|
this._setLovelace();
|
||||||
|
}
|
||||||
|
|
||||||
static readonly styles: CSSResultGroup = css`
|
static readonly styles: CSSResultGroup = css`
|
||||||
:host {
|
:host {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import "../../components/ha-settings-row";
|
|||||||
import "../../components/ha-switch";
|
import "../../components/ha-switch";
|
||||||
import type { CoreFrontendUserData } from "../../data/frontend";
|
import type { CoreFrontendUserData } from "../../data/frontend";
|
||||||
import { saveFrontendUserData } from "../../data/frontend";
|
import { saveFrontendUserData } from "../../data/frontend";
|
||||||
|
import { documentationUrl } from "../../util/documentation-url";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
@customElement("ha-advanced-mode-row")
|
@customElement("ha-advanced-mode-row")
|
||||||
@@ -31,7 +32,10 @@ class AdvancedModeRow extends LitElement {
|
|||||||
<span slot="description">
|
<span slot="description">
|
||||||
${this.hass.localize("ui.panel.profile.advanced_mode.description")}
|
${this.hass.localize("ui.panel.profile.advanced_mode.description")}
|
||||||
<a
|
<a
|
||||||
href="https://www.home-assistant.io/blog/2019/07/17/release-96/#advanced-mode"
|
href=${documentationUrl(
|
||||||
|
this.hass,
|
||||||
|
"/blog/2019/07/17/release-96/#advanced-mode"
|
||||||
|
)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>${this.hass.localize("ui.panel.profile.advanced_mode.link_promo")}
|
>${this.hass.localize("ui.panel.profile.advanced_mode.link_promo")}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class HaPickDashboardRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const value = this.hass.userData?.defaultPanel || USE_SYSTEM_VALUE;
|
const value = this.hass.userData?.default_panel || USE_SYSTEM_VALUE;
|
||||||
return html`
|
return html`
|
||||||
<ha-settings-row .narrow=${this.narrow}>
|
<ha-settings-row .narrow=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
@@ -84,12 +84,12 @@ class HaPickDashboardRow extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const urlPath = value === USE_SYSTEM_VALUE ? undefined : value;
|
const urlPath = value === USE_SYSTEM_VALUE ? undefined : value;
|
||||||
if (urlPath === this.hass.userData?.defaultPanel) {
|
if (urlPath === this.hass.userData?.default_panel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveFrontendUserData(this.hass.connection, "core", {
|
saveFrontendUserData(this.hass.connection, "core", {
|
||||||
...this.hass.userData,
|
...this.hass.userData,
|
||||||
defaultPanel: urlPath,
|
default_panel: urlPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,6 @@ export const waColorStyles = css`
|
|||||||
--wa-color-danger-on-normal: var(--ha-color-on-danger-normal);
|
--wa-color-danger-on-normal: var(--ha-color-on-danger-normal);
|
||||||
--wa-color-danger-on-quiet: var(--ha-color-on-danger-quiet);
|
--wa-color-danger-on-quiet: var(--ha-color-on-danger-quiet);
|
||||||
|
|
||||||
--wa-color-text-quiet: var(--ha-color-text-secondary);
|
|
||||||
|
|
||||||
--wa-color-text-normal: var(--ha-color-text-primary);
|
--wa-color-text-normal: var(--ha-color-text-primary);
|
||||||
--wa-color-surface-default: var(--card-background-color);
|
--wa-color-surface-default: var(--card-background-color);
|
||||||
--wa-color-surface-raised: var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff));
|
--wa-color-surface-raised: var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff));
|
||||||
@@ -64,7 +62,5 @@ export const waColorStyles = css`
|
|||||||
|
|
||||||
--wa-focus-ring-color: var(--ha-color-neutral-60);
|
--wa-focus-ring-color: var(--ha-color-neutral-60);
|
||||||
--wa-shadow-l: 4px 8px 12px 0 rgba(0, 0, 0, 0.3);
|
--wa-shadow-l: 4px 8px 12px 0 rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
--wa-color-text-normal: var(--ha-color-text-primary);
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -9,16 +9,12 @@ export const waMainStyles = css`
|
|||||||
--wa-focus-ring-offset: 2px;
|
--wa-focus-ring-offset: 2px;
|
||||||
--wa-focus-ring: var(--wa-focus-ring-style) var(--wa-focus-ring-width) var(--wa-focus-ring-color);
|
--wa-focus-ring: var(--wa-focus-ring-style) var(--wa-focus-ring-width) var(--wa-focus-ring-color);
|
||||||
|
|
||||||
--wa-space-xs: var(--ha-space-2);
|
|
||||||
--wa-space-m: var(--ha-space-4);
|
|
||||||
--wa-space-l: var(--ha-space-6);
|
--wa-space-l: var(--ha-space-6);
|
||||||
--wa-space-xl: var(--ha-space-8);
|
--wa-space-xl: var(--ha-space-8);
|
||||||
|
|
||||||
--wa-form-control-padding-block: 0.75em;
|
--wa-form-control-padding-block: 0.75em;
|
||||||
--wa-form-control-value-line-height: var(--ha-line-height-condensed);
|
|
||||||
|
|
||||||
--wa-font-weight-action: var(--ha-font-weight-medium);
|
--wa-font-weight-action: var(--ha-font-weight-medium);
|
||||||
--wa-transition-normal: 150ms;
|
|
||||||
--wa-transition-fast: 75ms;
|
--wa-transition-fast: 75ms;
|
||||||
--wa-transition-easing: ease;
|
--wa-transition-easing: ease;
|
||||||
|
|
||||||
@@ -32,7 +28,6 @@ export const waMainStyles = css`
|
|||||||
|
|
||||||
--wa-line-height-condensed: var(--ha-line-height-condensed);
|
--wa-line-height-condensed: var(--ha-line-height-condensed);
|
||||||
|
|
||||||
--wa-font-size-m: var(--ha-font-size-m);
|
|
||||||
--wa-shadow-s: var(--ha-box-shadow-s);
|
--wa-shadow-s: var(--ha-box-shadow-s);
|
||||||
--wa-shadow-m: var(--ha-box-shadow-m);
|
--wa-shadow-m: var(--ha-box-shadow-m);
|
||||||
--wa-shadow-l: var(--ha-box-shadow-l);
|
--wa-shadow-l: var(--ha-box-shadow-l);
|
||||||
|
|||||||
@@ -2220,6 +2220,14 @@
|
|||||||
"migrate_to_user_data": "This will change the sidebar on all the devices you are logged in to. To create a sidebar per device, you should use a different user for that device."
|
"migrate_to_user_data": "This will change the sidebar on all the devices you are logged in to. To create a sidebar per device, you should use a different user for that device."
|
||||||
},
|
},
|
||||||
"panel": {
|
"panel": {
|
||||||
|
"home": {
|
||||||
|
"editor": {
|
||||||
|
"title": "Edit home page",
|
||||||
|
"description": "Configure your home page display preferences.",
|
||||||
|
"favorite_entities_helper": "Display your favorite entities. Home Assistant will still suggest based on commonly used up to 8 slots.",
|
||||||
|
"save_failed": "Failed to save home page configuration"
|
||||||
|
}
|
||||||
|
},
|
||||||
"my": {
|
"my": {
|
||||||
"not_supported": "This redirect is not supported by your Home Assistant instance. Check the {link} for the supported redirects and the version they where introduced.",
|
"not_supported": "This redirect is not supported by your Home Assistant instance. Check the {link} for the supported redirects and the version they where introduced.",
|
||||||
"component_not_loaded": "This redirect is not supported by your Home Assistant instance. You need the integration {integration} to use this redirect.",
|
"component_not_loaded": "This redirect is not supported by your Home Assistant instance. You need the integration {integration} to use this redirect.",
|
||||||
@@ -3998,25 +4006,7 @@
|
|||||||
"item_pasted": "{item} pasted",
|
"item_pasted": "{item} pasted",
|
||||||
"ctrl": "Ctrl",
|
"ctrl": "Ctrl",
|
||||||
"del": "Del",
|
"del": "Del",
|
||||||
"targets": "Targets",
|
|
||||||
"select_target": "Select a target",
|
|
||||||
"home": "Home",
|
|
||||||
"unassigned": "Unassigned",
|
|
||||||
"blocks": "Blocks",
|
"blocks": "Blocks",
|
||||||
"show_more": "Show more",
|
|
||||||
"unassigned_entities": "Unassigned entities",
|
|
||||||
"unassigned_devices": "Unassigned devices",
|
|
||||||
"empty_section_search": {
|
|
||||||
"block": "No blocks found for {term}",
|
|
||||||
"entity": "No entities found for {term}",
|
|
||||||
"device": "No devices found for {term}",
|
|
||||||
"area": "No areas or floors found for {term}",
|
|
||||||
"label": "No labels found for {term}"
|
|
||||||
},
|
|
||||||
"load_target_items_failed": "Failed to load target items for",
|
|
||||||
"other_areas": "Other areas",
|
|
||||||
"services": "Services",
|
|
||||||
"helpers": "Helpers",
|
|
||||||
"triggers": {
|
"triggers": {
|
||||||
"name": "Triggers",
|
"name": "Triggers",
|
||||||
"header": "When",
|
"header": "When",
|
||||||
@@ -4024,10 +4014,7 @@
|
|||||||
"learn_more": "Learn more about triggers",
|
"learn_more": "Learn more about triggers",
|
||||||
"triggered": "Triggered",
|
"triggered": "Triggered",
|
||||||
"add": "Add trigger",
|
"add": "Add trigger",
|
||||||
"empty_search": {
|
"empty_search": "No triggers found for {term}",
|
||||||
"global": "No triggers and targets found for {term}",
|
|
||||||
"item": "No triggers found for {term}"
|
|
||||||
},
|
|
||||||
"id": "Trigger ID",
|
"id": "Trigger ID",
|
||||||
"optional": "Optional",
|
"optional": "Optional",
|
||||||
"edit_id": "Edit ID",
|
"edit_id": "Edit ID",
|
||||||
@@ -4048,7 +4035,6 @@
|
|||||||
"copied_to_clipboard": "Trigger copied to clipboard",
|
"copied_to_clipboard": "Trigger copied to clipboard",
|
||||||
"cut_to_clipboard": "Trigger cut to clipboard",
|
"cut_to_clipboard": "Trigger cut to clipboard",
|
||||||
"select": "Select a trigger",
|
"select": "Select a trigger",
|
||||||
"no_items_for_target": "No triggers available for",
|
|
||||||
"groups": {
|
"groups": {
|
||||||
"device": {
|
"device": {
|
||||||
"label": "Device"
|
"label": "Device"
|
||||||
@@ -4290,10 +4276,7 @@
|
|||||||
"description": "All conditions added here need to be satisfied for the automation to run. A condition can be satisfied or not at any given time, for example: ''If {user} is home''. You can use building blocks to create more complex conditions.",
|
"description": "All conditions added here need to be satisfied for the automation to run. A condition can be satisfied or not at any given time, for example: ''If {user} is home''. You can use building blocks to create more complex conditions.",
|
||||||
"learn_more": "Learn more about conditions",
|
"learn_more": "Learn more about conditions",
|
||||||
"add": "Add condition",
|
"add": "Add condition",
|
||||||
"empty_search": {
|
"empty_search": "No conditions and blocks found for {term}",
|
||||||
"global": "No conditions, blocks and targets found for {term}",
|
|
||||||
"item": "No conditions found for {term}"
|
|
||||||
},
|
|
||||||
"add_building_block": "Add building block",
|
"add_building_block": "Add building block",
|
||||||
"test": "Test",
|
"test": "Test",
|
||||||
"testing_error": "Condition did not pass",
|
"testing_error": "Condition did not pass",
|
||||||
@@ -4316,7 +4299,6 @@
|
|||||||
"copied_to_clipboard": "Condition copied to clipboard",
|
"copied_to_clipboard": "Condition copied to clipboard",
|
||||||
"cut_to_clipboard": "Condition cut to clipboard",
|
"cut_to_clipboard": "Condition cut to clipboard",
|
||||||
"select": "Select a condition",
|
"select": "Select a condition",
|
||||||
"no_items_for_target": "No conditions available for",
|
|
||||||
"groups": {
|
"groups": {
|
||||||
"device": {
|
"device": {
|
||||||
"label": "Device"
|
"label": "Device"
|
||||||
@@ -4462,10 +4444,7 @@
|
|||||||
"description": "All actions added here will be performed in sequence when the automation runs. An action usually controls one of your areas, devices, or entities, for example: 'Turn on the lights'. You can use building blocks to create more complex sequences of actions.",
|
"description": "All actions added here will be performed in sequence when the automation runs. An action usually controls one of your areas, devices, or entities, for example: 'Turn on the lights'. You can use building blocks to create more complex sequences of actions.",
|
||||||
"learn_more": "Learn more about actions",
|
"learn_more": "Learn more about actions",
|
||||||
"add": "Add action",
|
"add": "Add action",
|
||||||
"empty_search": {
|
"empty_search": "No actions and blocks found for {term}",
|
||||||
"global": "No actions, blocks and targets found for {term}",
|
|
||||||
"item": "No actions found for {term}"
|
|
||||||
},
|
|
||||||
"add_building_block": "Add building block",
|
"add_building_block": "Add building block",
|
||||||
"invalid_action": "Invalid action",
|
"invalid_action": "Invalid action",
|
||||||
"run": "Run action",
|
"run": "Run action",
|
||||||
@@ -4490,7 +4469,6 @@
|
|||||||
"copied_to_clipboard": "Action copied to clipboard",
|
"copied_to_clipboard": "Action copied to clipboard",
|
||||||
"cut_to_clipboard": "Action cut to clipboard",
|
"cut_to_clipboard": "Action cut to clipboard",
|
||||||
"select": "Select an action",
|
"select": "Select an action",
|
||||||
"no_items_for_target": "No actions available for",
|
|
||||||
"groups": {
|
"groups": {
|
||||||
"device_id": {
|
"device_id": {
|
||||||
"label": "Device"
|
"label": "Device"
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -1940,9 +1940,9 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@home-assistant/webawesome@npm:3.0.0-ha.0":
|
"@home-assistant/webawesome@npm:3.0.0":
|
||||||
version: 3.0.0-ha.0
|
version: 3.0.0
|
||||||
resolution: "@home-assistant/webawesome@npm:3.0.0-ha.0"
|
resolution: "@home-assistant/webawesome@npm:3.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ctrl/tinycolor": "npm:4.1.0"
|
"@ctrl/tinycolor": "npm:4.1.0"
|
||||||
"@floating-ui/dom": "npm:^1.6.13"
|
"@floating-ui/dom": "npm:^1.6.13"
|
||||||
@@ -1953,7 +1953,7 @@ __metadata:
|
|||||||
lit: "npm:^3.2.1"
|
lit: "npm:^3.2.1"
|
||||||
nanoid: "npm:^5.1.5"
|
nanoid: "npm:^5.1.5"
|
||||||
qr-creator: "npm:^1.0.0"
|
qr-creator: "npm:^1.0.0"
|
||||||
checksum: 10/2034d498d5b26bb0573ebc2c9aadd144604bb48c04becbae0c67b16857d8e5d6562626e795974362c3fc41e9b593a9005595d8b5ff434b1569b2d724af13043b
|
checksum: 10/03400894cfee8548fd5b1f5c56d31d253830e704b18ba69d36ce6b761d8b1bef2fb52cffba8d9b033033bb582f2f51a2d6444d82622f66d70150e2104fcb49e2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -9226,7 +9226,7 @@ __metadata:
|
|||||||
"@fullcalendar/list": "npm:6.1.19"
|
"@fullcalendar/list": "npm:6.1.19"
|
||||||
"@fullcalendar/luxon3": "npm:6.1.19"
|
"@fullcalendar/luxon3": "npm:6.1.19"
|
||||||
"@fullcalendar/timegrid": "npm:6.1.19"
|
"@fullcalendar/timegrid": "npm:6.1.19"
|
||||||
"@home-assistant/webawesome": "npm:3.0.0-ha.0"
|
"@home-assistant/webawesome": "npm:3.0.0"
|
||||||
"@lezer/highlight": "npm:1.2.3"
|
"@lezer/highlight": "npm:1.2.3"
|
||||||
"@lit-labs/motion": "npm:1.0.9"
|
"@lit-labs/motion": "npm:1.0.9"
|
||||||
"@lit-labs/observers": "npm:2.0.6"
|
"@lit-labs/observers": "npm:2.0.6"
|
||||||
|
|||||||
Reference in New Issue
Block a user