From 579050bfc7f2cb63863699c42c03b8f0cd1eefb6 Mon Sep 17 00:00:00 2001
From: karwosts <32912880+karwosts@users.noreply.github.com>
Date: Mon, 25 Sep 2023 08:26:53 -0700
Subject: [PATCH] Add new config options for map entity markers (#17938)
---
src/components/map/ha-entity-marker.ts | 6 ++-
src/components/map/ha-map.ts | 51 ++++++++++++-------
src/panels/lovelace/cards/hui-map-card.ts | 42 ++++++++-------
.../config-elements/hui-map-card-editor.ts | 14 ++++-
4 files changed, 74 insertions(+), 39 deletions(-)
diff --git a/src/components/map/ha-entity-marker.ts b/src/components/map/ha-entity-marker.ts
index 3524c52a21..248ace4af7 100644
--- a/src/components/map/ha-entity-marker.ts
+++ b/src/components/map/ha-entity-marker.ts
@@ -15,7 +15,7 @@ class HaEntityMarker extends LitElement {
protected render() {
return html`
@@ -45,7 +45,6 @@ class HaEntityMarker extends LitElement {
justify-content: center;
align-items: center;
box-sizing: border-box;
- overflow: hidden;
width: 48px;
height: 48px;
font-size: var(--ha-marker-font-size, 1.5em);
@@ -54,6 +53,9 @@ class HaEntityMarker extends LitElement {
color: var(--primary-text-color);
background-color: var(--card-background-color);
}
+ .marker.picture {
+ overflow: hidden;
+ }
.entity-picture {
background-size: cover;
height: 100%;
diff --git a/src/components/map/ha-map.ts b/src/components/map/ha-map.ts
index 8ff0b4275c..14240a90f2 100644
--- a/src/components/map/ha-map.ts
+++ b/src/components/map/ha-map.ts
@@ -37,6 +37,9 @@ export interface HaMapPaths {
export interface HaMapEntity {
entity_id: string;
color: string;
+ label_mode?: "name" | "state";
+ name?: string;
+ focus?: boolean;
}
@customElement("ha-map")
@@ -71,6 +74,8 @@ export class HaMap extends ReactiveElement {
private _mapItems: Array
= [];
+ private _mapFocusItems: Array = [];
+
private _mapZones: Array = [];
private _mapPaths: Array = [];
@@ -168,7 +173,7 @@ export class HaMap extends ReactiveElement {
return;
}
- if (!this._mapItems.length && !this.layers?.length) {
+ if (!this._mapFocusItems.length && !this.layers?.length) {
this.leafletMap.setView(
new this.Leaflet.LatLng(
this.hass.config.latitude,
@@ -180,7 +185,9 @@ export class HaMap extends ReactiveElement {
}
let bounds = this.Leaflet.latLngBounds(
- this._mapItems ? this._mapItems.map((item) => item.getLatLng()) : []
+ this._mapFocusItems
+ ? this._mapFocusItems.map((item) => item.getLatLng())
+ : []
);
if (this.fitZones) {
@@ -324,6 +331,7 @@ export class HaMap extends ReactiveElement {
if (this._mapItems.length) {
this._mapItems.forEach((marker) => marker.remove());
this._mapItems = [];
+ this._mapFocusItems = [];
}
if (this._mapZones.length) {
@@ -353,7 +361,8 @@ export class HaMap extends ReactiveElement {
if (!stateObj) {
continue;
}
- const title = computeStateName(stateObj);
+ const customTitle = typeof entity !== "string" ? entity.name : undefined;
+ const title = customTitle ?? computeStateName(stateObj);
const {
latitude,
longitude,
@@ -413,17 +422,20 @@ export class HaMap extends ReactiveElement {
// DRAW ENTITY
// create icon
- const entityName = title
- .split(" ")
- .map((part) => part[0])
- .join("")
- .substr(0, 3);
+ const entityName =
+ typeof entity !== "string" && entity.label_mode === "state"
+ ? this.hass.formatEntityState(stateObj)
+ : customTitle ??
+ title
+ .split(" ")
+ .map((part) => part[0])
+ .join("")
+ .substr(0, 3);
// create marker with the icon
- this._mapItems.push(
- Leaflet.marker([latitude, longitude], {
- icon: Leaflet.divIcon({
- html: `
+ const marker = Leaflet.marker([latitude, longitude], {
+ icon: Leaflet.divIcon({
+ html: `
`,
- iconSize: [48, 48],
- className: "",
- }),
- title: computeStateName(stateObj),
- })
- );
+ iconSize: [48, 48],
+ className: "",
+ }),
+ title: title,
+ });
+ this._mapItems.push(marker);
+ if (typeof entity === "string" || entity.focus !== false) {
+ this._mapFocusItems.push(marker);
+ }
// create circle around if entity has accuracy
if (gpsAccuracy) {
diff --git a/src/panels/lovelace/cards/hui-map-card.ts b/src/panels/lovelace/cards/hui-map-card.ts
index ec9dfa6138..6902198eba 100644
--- a/src/panels/lovelace/cards/hui-map-card.ts
+++ b/src/panels/lovelace/cards/hui-map-card.ts
@@ -48,6 +48,11 @@ import { MapCardConfig } from "./types";
export const DEFAULT_HOURS_TO_SHOW = 0;
export const DEFAULT_ZOOM = 14;
+interface MapEntityConfig extends EntityConfig {
+ label_mode?: "state" | "name";
+ focus?: boolean;
+}
+
@customElement("hui-map-card")
class HuiMapCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -63,7 +68,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
@query("ha-map")
private _map?: HaMap;
- private _configEntities?: string[];
+ private _configEntities?: MapEntityConfig[];
private _colorDict: Record = {};
@@ -94,11 +99,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
}
this._config = config;
- this._configEntities = (
- config.entities
- ? processConfigEntities(config.entities)
- : []
- ).map((entity) => entity.entity);
+ this._configEntities = config.entities
+ ? processConfigEntities(config.entities)
+ : [];
}
public getCardSize(): number {
@@ -238,7 +241,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
this._stateHistory = combinedHistory;
},
this._config!.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW,
- this._configEntities!,
+ (this._configEntities || []).map((entity) => entity.entity)!,
false,
false,
false
@@ -309,16 +312,14 @@ class HuiMapCard extends LitElement implements LovelaceCard {
(
states: HassEntities,
config: MapCardConfig,
- configEntities?: string[]
+ configEntities?: MapEntityConfig[]
) => {
if (!states || !config) {
return undefined;
}
- let entities = configEntities || [];
-
+ const geoEntities: string[] = [];
if (config.geo_location_sources) {
- const geoEntities: string[] = [];
// Calculate visible geo location sources
const includesAll = config.geo_location_sources.includes("all");
for (const stateObj of Object.values(states)) {
@@ -330,14 +331,21 @@ class HuiMapCard extends LitElement implements LovelaceCard {
geoEntities.push(stateObj.entity_id);
}
}
-
- entities = [...entities, ...geoEntities];
}
- return entities.map((entity) => ({
- entity_id: entity,
- color: this._getColor(entity),
- }));
+ return [
+ ...(configEntities || []).map((entityConf) => ({
+ entity_id: entityConf.entity,
+ color: this._getColor(entityConf.entity),
+ label_mode: entityConf.label_mode,
+ focus: entityConf.focus,
+ name: entityConf.name,
+ })),
+ ...geoEntities.map((entity) => ({
+ entity_id: entity,
+ color: this._getColor(entity),
+ })),
+ ];
}
);
diff --git a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
index fb4533a1f7..7fb605e3f8 100644
--- a/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
+++ b/src/panels/lovelace/editor/config-elements/hui-map-card-editor.ts
@@ -9,6 +9,7 @@ import {
object,
optional,
string,
+ union,
} from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { hasLocation } from "../../../../common/entity/has_location";
@@ -25,10 +26,19 @@ import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types";
import { processEditorEntities } from "../process-editor-entities";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
-import { entitiesConfigStruct } from "../structs/entities-struct";
import { EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style";
+export const mapEntitiesConfigStruct = union([
+ object({
+ entity: string(),
+ label_mode: optional(string()),
+ focus: optional(boolean()),
+ name: optional(string()),
+ }),
+ string(),
+]);
+
const cardConfigStruct = assign(
baseLovelaceCardConfig,
object({
@@ -36,7 +46,7 @@ const cardConfigStruct = assign(
aspect_ratio: optional(string()),
default_zoom: optional(number()),
dark_mode: optional(boolean()),
- entities: array(entitiesConfigStruct),
+ entities: array(mapEntitiesConfigStruct),
hours_to_show: optional(number()),
geo_location_sources: optional(array(string())),
auto_fit: optional(boolean()),