diff --git a/src/panels/config/dashboard/dialog-new-dashboard.ts b/src/panels/config/dashboard/dialog-new-dashboard.ts
index 89a74d0a50..10b65cee0c 100644
--- a/src/panels/config/dashboard/dialog-new-dashboard.ts
+++ b/src/panels/config/dashboard/dialog-new-dashboard.ts
@@ -1,20 +1,32 @@
import "@material/mwc-list/mwc-list";
-import { mdiPencilOutline, mdiShape } from "@mdi/js";
+import { mdiMap, mdiPencilOutline, mdiShape } from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
+import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-icon-next";
import "../../../components/ha-list-item";
+import { LovelaceRawConfig } from "../../../data/lovelace/config/types";
import { HassDialog } from "../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
-import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import { NewDashboardDialogParams } from "./show-dialog-new-dashboard";
-import { LovelaceRawConfig } from "../../../data/lovelace/config/types";
const EMPTY_CONFIG: LovelaceRawConfig = { views: [{ title: "Home" }] };
+type Strategy = {
+ type: string;
+ iconPath: string;
+};
+
+const STRATEGIES = [
+ {
+ type: "map",
+ iconPath: mdiMap,
+ },
+] as const satisfies Strategy[];
+
@customElement("ha-dialog-new-dashboard")
class DialogNewDashboard extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -100,16 +112,55 @@ class DialogNewDashboard extends LitElement implements HassDialog {
>
+ ${STRATEGIES.map(
+ (strategy) => html`
+
+
+ ${this.hass.localize(
+ `ui.panel.config.lovelace.dashboards.dialog_new.strategy.${strategy.type}.title`
+ )}
+
+ ${this.hass.localize(
+ `ui.panel.config.lovelace.dashboards.dialog_new.strategy.${strategy.type}.description`
+ )}
+
+
+
+ `
+ )}
`;
}
+ private _generateStrategyConfig(strategy: string) {
+ return {
+ strategy: {
+ type: strategy,
+ },
+ };
+ }
+
private async _selected(ev) {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
- const config = (ev.currentTarget! as any).config;
+
+ const target = ev.currentTarget as any;
+ const config =
+ target.config ||
+ (target.strategy && this._generateStrategyConfig(target.strategy)) ||
+ null;
+
this._params?.selectConfig(config);
this.closeDialog();
}
diff --git a/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts
similarity index 83%
rename from src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts
rename to src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts
index e114547246..0a826b11b0 100644
--- a/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts
+++ b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/dialog-dashboard-strategy-editor.ts
@@ -6,22 +6,22 @@ import {
} from "@mdi/js";
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
-import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
-import { stopPropagation } from "../../../../common/dom/stop_propagation";
-import "../../../../components/ha-button";
-import "../../../../components/ha-button-menu";
-import "../../../../components/ha-dialog";
-import "../../../../components/ha-dialog-header";
-import "../../../../components/ha-icon-button";
-import { LovelaceStrategyConfig } from "../../../../data/lovelace/config/strategy";
-import { haStyleDialog } from "../../../../resources/styles";
-import type { HomeAssistant } from "../../../../types";
-import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
-import "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor";
-import type { HuiDashboardStrategyElementEditor } from "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor";
-import { ConfigChangedEvent } from "../../editor/hui-element-editor";
-import { GUIModeChangedEvent } from "../../editor/types";
-import { cleanLegacyStrategyConfig } from "../legacy-strategy";
+import { HASSDomEvent, fireEvent } from "../../../../../common/dom/fire_event";
+import { stopPropagation } from "../../../../../common/dom/stop_propagation";
+import "../../../../../components/ha-button";
+import "../../../../../components/ha-button-menu";
+import "../../../../../components/ha-dialog";
+import "../../../../../components/ha-dialog-header";
+import "../../../../../components/ha-icon-button";
+import { LovelaceStrategyConfig } from "../../../../../data/lovelace/config/strategy";
+import { haStyleDialog } from "../../../../../resources/styles";
+import type { HomeAssistant } from "../../../../../types";
+import { showSaveSuccessToast } from "../../../../../util/toast-saved-success";
+import "../hui-dashboard-strategy-element-editor";
+import type { HuiDashboardStrategyElementEditor } from "../hui-dashboard-strategy-element-editor";
+import { ConfigChangedEvent } from "../../hui-element-editor";
+import { GUIModeChangedEvent } from "../../types";
+import { cleanLegacyStrategyConfig } from "../../../strategies/legacy-strategy";
import type { DashboardStrategyEditorDialogParams } from "./show-dialog-dashboard-strategy-editor";
@customElement("dialog-dashboard-strategy-editor")
diff --git a/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/show-dialog-dashboard-strategy-editor.ts
similarity index 80%
rename from src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts
rename to src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/show-dialog-dashboard-strategy-editor.ts
index 9ad34cc51d..d7571464f9 100644
--- a/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts
+++ b/src/panels/lovelace/editor/dashboard-strategy-editor/dialogs/show-dialog-dashboard-strategy-editor.ts
@@ -1,5 +1,5 @@
-import { fireEvent } from "../../../../common/dom/fire_event";
-import { LovelaceDashboardStrategyConfig } from "../../../../data/lovelace/config/types";
+import { fireEvent } from "../../../../../common/dom/fire_event";
+import { LovelaceDashboardStrategyConfig } from "../../../../../data/lovelace/config/types";
export interface DashboardStrategyEditorDialogParams {
config: LovelaceDashboardStrategyConfig;
diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts
index 48c18f8424..c5abdd4409 100644
--- a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts
+++ b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts
@@ -7,7 +7,7 @@ import type {
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
-import { OriginalStatesDashboardStrategyConfig } from "../../strategies/original-states-dashboard-strategy";
+import { OriginalStatesDashboardStrategyConfig } from "../../strategies/original-states/original-states-dashboard-strategy";
import { LovelaceStrategyEditor } from "../../strategies/types";
const SCHEMA = [
@@ -38,7 +38,7 @@ const SCHEMA = [
] as const satisfies readonly HaFormSchema[];
@customElement("hui-original-states-dashboard-strategy-editor")
-export class HuiOriginalStatesDashboarStrategyEditor
+export class HuiOriginalStatesDashboardStrategyEditor
extends LitElement
implements LovelaceStrategyEditor
{
@@ -88,6 +88,6 @@ export class HuiOriginalStatesDashboarStrategyEditor
declare global {
interface HTMLElementTagNameMap {
- "hui-original-states-dashboard-strategy-editor": HuiOriginalStatesDashboarStrategyEditor;
+ "hui-original-states-dashboard-strategy-editor": HuiOriginalStatesDashboardStrategyEditor;
}
}
diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts
index 8257f0e2cb..b0115082bb 100644
--- a/src/panels/lovelace/hui-root.ts
+++ b/src/panels/lovelace/hui-root.ts
@@ -63,7 +63,7 @@ import { documentationUrl } from "../../util/documentation-url";
import { swapView } from "./editor/config-util";
import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
-import { showDashboardStrategyEditorDialog } from "./strategies/device-registry-detail/show-dialog-dashboard-strategy-editor";
+import { showDashboardStrategyEditorDialog } from "./editor/dashboard-strategy-editor/dialogs/show-dialog-dashboard-strategy-editor";
import type { Lovelace } from "./types";
import "./views/hui-view";
import type { HUIView } from "./views/hui-view";
@@ -75,6 +75,7 @@ import {
import { showSaveDialog } from "./editor/show-save-config-dialog";
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
import { LocalizeKeys } from "../../common/translations/localize";
+import { getLovelaceStrategy } from "./strategies/get-strategy";
@customElement("hui-root")
class HUIRoot extends LitElement {
@@ -709,7 +710,7 @@ class HUIRoot extends LitElement {
this._enableEditMode();
}
- private _enableEditMode(): void {
+ private async _enableEditMode() {
if (this._yamlMode) {
showAlertDialog(this, {
text: this.hass!.localize("ui.panel.lovelace.editor.yaml_unsupported"),
@@ -720,6 +721,18 @@ class HUIRoot extends LitElement {
isStrategyDashboard(this.lovelace!.rawConfig) &&
!isLegacyStrategyConfig(this.lovelace!.rawConfig.strategy)
) {
+ const strategyClass = await getLovelaceStrategy(
+ "dashboard",
+ this.lovelace!.rawConfig.strategy.type
+ ).catch((_err) => undefined);
+ if (strategyClass?.noEditor) {
+ showSaveDialog(this, {
+ lovelace: this.lovelace!,
+ mode: "storage",
+ narrow: this.narrow!,
+ });
+ return;
+ }
showDashboardStrategyEditorDialog(this, {
config: this.lovelace!.rawConfig,
saveConfig: this.lovelace!.saveConfig,
diff --git a/src/panels/lovelace/strategies/get-strategy.ts b/src/panels/lovelace/strategies/get-strategy.ts
index 927089dedd..5613d23111 100644
--- a/src/panels/lovelace/strategies/get-strategy.ts
+++ b/src/panels/lovelace/strategies/get-strategy.ts
@@ -22,11 +22,15 @@ const CUSTOM_PREFIX = "custom:";
const STRATEGIES: Record> = {
dashboard: {
- "original-states": () => import("./original-states-dashboard-strategy"),
+ "original-states": () =>
+ import("./original-states/original-states-dashboard-strategy"),
+ map: () => import("./map/map-dashboard-strategy"),
},
view: {
- "original-states": () => import("./original-states-view-strategy"),
+ "original-states": () =>
+ import("./original-states/original-states-view-strategy"),
energy: () => import("../../energy/strategies/energy-view-strategy"),
+ map: () => import("./map/map-view-strategy"),
},
section: {},
};
diff --git a/src/panels/lovelace/strategies/map/map-dashboard-strategy.ts b/src/panels/lovelace/strategies/map/map-dashboard-strategy.ts
new file mode 100644
index 0000000000..91c217ce08
--- /dev/null
+++ b/src/panels/lovelace/strategies/map/map-dashboard-strategy.ts
@@ -0,0 +1,32 @@
+import { ReactiveElement } from "lit";
+import { customElement } from "lit/decorators";
+import { LovelaceConfig } from "../../../../data/lovelace/config/types";
+import { HomeAssistant } from "../../../../types";
+import { MapViewStrategyConfig } from "./map-view-strategy";
+
+export type MapDashboardStrategyConfig = MapViewStrategyConfig;
+
+@customElement("map-dashboard-strategy")
+export class MapDashboardStrategy extends ReactiveElement {
+ static async generate(
+ config: MapDashboardStrategyConfig,
+ hass: HomeAssistant
+ ): Promise {
+ return {
+ title: hass.localize("panel.map"),
+ views: [
+ {
+ strategy: config,
+ },
+ ],
+ };
+ }
+
+ static noEditor = true;
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "map-dashboard-strategy": MapDashboardStrategy;
+ }
+}
diff --git a/src/panels/lovelace/strategies/map/map-view-strategy.ts b/src/panels/lovelace/strategies/map/map-view-strategy.ts
new file mode 100644
index 0000000000..e9776fa030
--- /dev/null
+++ b/src/panels/lovelace/strategies/map/map-view-strategy.ts
@@ -0,0 +1,58 @@
+import { ReactiveElement } from "lit";
+import { customElement } from "lit/decorators";
+import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
+import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
+import { HomeAssistant } from "../../../../types";
+import { MapCardConfig } from "../../cards/types";
+
+export type MapViewStrategyConfig = {
+ type: "map";
+};
+
+const getMapEntities = (hass: HomeAssistant) => {
+ const personSources = new Set();
+ const locationEntities: string[] = [];
+ Object.values(hass.states).forEach((entity) => {
+ if (
+ entity.state === "home" ||
+ !("latitude" in entity.attributes) ||
+ !("longitude" in entity.attributes)
+ ) {
+ return;
+ }
+ locationEntities.push(entity.entity_id);
+ if (computeStateDomain(entity) === "person" && entity.attributes.source) {
+ personSources.add(entity.attributes.source);
+ }
+ });
+
+ return locationEntities.filter((entity) => !personSources.has(entity));
+};
+
+@customElement("map-view-strategy")
+export class MapViewStrategy extends ReactiveElement {
+ static async generate(
+ _config: MapViewStrategyConfig,
+ hass: HomeAssistant
+ ): Promise {
+ const entities = getMapEntities(hass);
+ return {
+ type: "panel",
+ title: hass.localize("panel.map"),
+ icon: "mdi:map",
+ cards: [
+ {
+ type: "map",
+ auto_fit: true,
+ entities: entities,
+ } as MapCardConfig,
+ ],
+ };
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "map-view-strategy": MapViewStrategy;
+ }
+}
diff --git a/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts b/src/panels/lovelace/strategies/original-states/original-states-dashboard-strategy.ts
similarity index 78%
rename from src/panels/lovelace/strategies/original-states-dashboard-strategy.ts
rename to src/panels/lovelace/strategies/original-states/original-states-dashboard-strategy.ts
index 0c7ae3c14f..3f7faae9f4 100644
--- a/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts
+++ b/src/panels/lovelace/strategies/original-states/original-states-dashboard-strategy.ts
@@ -1,9 +1,9 @@
import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
-import { LovelaceConfig } from "../../../data/lovelace/config/types";
-import { HomeAssistant } from "../../../types";
+import { LovelaceConfig } from "../../../../data/lovelace/config/types";
+import { HomeAssistant } from "../../../../types";
import { OriginalStatesViewStrategyConfig } from "./original-states-view-strategy";
-import { LovelaceStrategyEditor } from "./types";
+import { LovelaceStrategyEditor } from "../types";
export type OriginalStatesDashboardStrategyConfig =
OriginalStatesViewStrategyConfig;
@@ -26,7 +26,7 @@ export class OriginalStatesDashboardStrategy extends ReactiveElement {
public static async getConfigElement(): Promise {
await import(
- "../editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor"
+ "../../editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor"
);
return document.createElement(
"hui-original-states-dashboard-strategy-editor"
diff --git a/src/panels/lovelace/strategies/original-states-view-strategy.ts b/src/panels/lovelace/strategies/original-states/original-states-view-strategy.ts
similarity index 82%
rename from src/panels/lovelace/strategies/original-states-view-strategy.ts
rename to src/panels/lovelace/strategies/original-states/original-states-view-strategy.ts
index 386f953fcf..a9c64793ce 100644
--- a/src/panels/lovelace/strategies/original-states-view-strategy.ts
+++ b/src/panels/lovelace/strategies/original-states/original-states-view-strategy.ts
@@ -1,12 +1,12 @@
import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
-import { isComponentLoaded } from "../../../common/config/is_component_loaded";
-import type { AreaFilterValue } from "../../../components/ha-area-filter";
-import { getEnergyPreferences } from "../../../data/energy";
-import { LovelaceViewConfig } from "../../../data/lovelace/config/view";
-import { HomeAssistant } from "../../../types";
-import { generateDefaultViewConfig } from "../common/generate-lovelace-config";
+import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
+import type { AreaFilterValue } from "../../../../components/ha-area-filter";
+import { getEnergyPreferences } from "../../../../data/energy";
+import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
+import { HomeAssistant } from "../../../../types";
+import { generateDefaultViewConfig } from "../../common/generate-lovelace-config";
export type OriginalStatesViewStrategyConfig = {
type: "original-states";
diff --git a/src/panels/lovelace/strategies/types.ts b/src/panels/lovelace/strategies/types.ts
index ae1be1bbca..ac3a07de37 100644
--- a/src/panels/lovelace/strategies/types.ts
+++ b/src/panels/lovelace/strategies/types.ts
@@ -8,6 +8,7 @@ import { LovelaceGenericElementEditor } from "../types";
export type LovelaceStrategy = {
generate(config: LovelaceStrategyConfig, hass: HomeAssistant): Promise;
getConfigElement?: () => LovelaceStrategyEditor;
+ noEditor?: boolean;
};
export interface LovelaceDashboardStrategy
diff --git a/src/translations/en.json b/src/translations/en.json
index fe9874df11..c016bba08a 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -2207,7 +2207,13 @@
"create_empty": "New dashboard from scratch",
"create_empty_description": "Start with an empty dashboard from scratch",
"default": "Default dashboard",
- "default_description": "Display your devices grouped by area"
+ "default_description": "Display your devices grouped by area",
+ "strategy": {
+ "map": {
+ "title": "[%key:panel::map%]",
+ "description": "Display people and your devices on a map"
+ }
+ }
},
"picker": {
"headers": {