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()),