Add new config options for map entity markers (#17938)

This commit is contained in:
karwosts 2023-09-25 08:26:53 -07:00 committed by GitHub
parent 6b33b4e656
commit 579050bfc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 39 deletions

View File

@ -15,7 +15,7 @@ class HaEntityMarker extends LitElement {
protected render() { protected render() {
return html` return html`
<div <div
class="marker" class="marker ${this.entityPicture ? "picture" : ""}"
style=${styleMap({ "border-color": this.entityColor })} style=${styleMap({ "border-color": this.entityColor })}
@click=${this._badgeTap} @click=${this._badgeTap}
> >
@ -45,7 +45,6 @@ class HaEntityMarker extends LitElement {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden;
width: 48px; width: 48px;
height: 48px; height: 48px;
font-size: var(--ha-marker-font-size, 1.5em); font-size: var(--ha-marker-font-size, 1.5em);
@ -54,6 +53,9 @@ class HaEntityMarker extends LitElement {
color: var(--primary-text-color); color: var(--primary-text-color);
background-color: var(--card-background-color); background-color: var(--card-background-color);
} }
.marker.picture {
overflow: hidden;
}
.entity-picture { .entity-picture {
background-size: cover; background-size: cover;
height: 100%; height: 100%;

View File

@ -37,6 +37,9 @@ export interface HaMapPaths {
export interface HaMapEntity { export interface HaMapEntity {
entity_id: string; entity_id: string;
color: string; color: string;
label_mode?: "name" | "state";
name?: string;
focus?: boolean;
} }
@customElement("ha-map") @customElement("ha-map")
@ -71,6 +74,8 @@ export class HaMap extends ReactiveElement {
private _mapItems: Array<Marker | Circle> = []; private _mapItems: Array<Marker | Circle> = [];
private _mapFocusItems: Array<Marker | Circle> = [];
private _mapZones: Array<Marker | Circle> = []; private _mapZones: Array<Marker | Circle> = [];
private _mapPaths: Array<Polyline | CircleMarker> = []; private _mapPaths: Array<Polyline | CircleMarker> = [];
@ -168,7 +173,7 @@ export class HaMap extends ReactiveElement {
return; return;
} }
if (!this._mapItems.length && !this.layers?.length) { if (!this._mapFocusItems.length && !this.layers?.length) {
this.leafletMap.setView( this.leafletMap.setView(
new this.Leaflet.LatLng( new this.Leaflet.LatLng(
this.hass.config.latitude, this.hass.config.latitude,
@ -180,7 +185,9 @@ export class HaMap extends ReactiveElement {
} }
let bounds = this.Leaflet.latLngBounds( let bounds = this.Leaflet.latLngBounds(
this._mapItems ? this._mapItems.map((item) => item.getLatLng()) : [] this._mapFocusItems
? this._mapFocusItems.map((item) => item.getLatLng())
: []
); );
if (this.fitZones) { if (this.fitZones) {
@ -324,6 +331,7 @@ export class HaMap extends ReactiveElement {
if (this._mapItems.length) { if (this._mapItems.length) {
this._mapItems.forEach((marker) => marker.remove()); this._mapItems.forEach((marker) => marker.remove());
this._mapItems = []; this._mapItems = [];
this._mapFocusItems = [];
} }
if (this._mapZones.length) { if (this._mapZones.length) {
@ -353,7 +361,8 @@ export class HaMap extends ReactiveElement {
if (!stateObj) { if (!stateObj) {
continue; continue;
} }
const title = computeStateName(stateObj); const customTitle = typeof entity !== "string" ? entity.name : undefined;
const title = customTitle ?? computeStateName(stateObj);
const { const {
latitude, latitude,
longitude, longitude,
@ -413,17 +422,20 @@ export class HaMap extends ReactiveElement {
// DRAW ENTITY // DRAW ENTITY
// create icon // create icon
const entityName = title const entityName =
.split(" ") typeof entity !== "string" && entity.label_mode === "state"
.map((part) => part[0]) ? this.hass.formatEntityState(stateObj)
.join("") : customTitle ??
.substr(0, 3); title
.split(" ")
.map((part) => part[0])
.join("")
.substr(0, 3);
// create marker with the icon // create marker with the icon
this._mapItems.push( const marker = Leaflet.marker([latitude, longitude], {
Leaflet.marker([latitude, longitude], { icon: Leaflet.divIcon({
icon: Leaflet.divIcon({ html: `
html: `
<ha-entity-marker <ha-entity-marker
entity-id="${getEntityId(entity)}" entity-id="${getEntityId(entity)}"
entity-name="${entityName}" entity-name="${entityName}"
@ -437,12 +449,15 @@ export class HaMap extends ReactiveElement {
} }
></ha-entity-marker> ></ha-entity-marker>
`, `,
iconSize: [48, 48], iconSize: [48, 48],
className: "", className: "",
}), }),
title: computeStateName(stateObj), title: title,
}) });
); this._mapItems.push(marker);
if (typeof entity === "string" || entity.focus !== false) {
this._mapFocusItems.push(marker);
}
// create circle around if entity has accuracy // create circle around if entity has accuracy
if (gpsAccuracy) { if (gpsAccuracy) {

View File

@ -48,6 +48,11 @@ import { MapCardConfig } from "./types";
export const DEFAULT_HOURS_TO_SHOW = 0; export const DEFAULT_HOURS_TO_SHOW = 0;
export const DEFAULT_ZOOM = 14; export const DEFAULT_ZOOM = 14;
interface MapEntityConfig extends EntityConfig {
label_mode?: "state" | "name";
focus?: boolean;
}
@customElement("hui-map-card") @customElement("hui-map-card")
class HuiMapCard extends LitElement implements LovelaceCard { class HuiMapCard extends LitElement implements LovelaceCard {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@ -63,7 +68,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
@query("ha-map") @query("ha-map")
private _map?: HaMap; private _map?: HaMap;
private _configEntities?: string[]; private _configEntities?: MapEntityConfig[];
private _colorDict: Record<string, string> = {}; private _colorDict: Record<string, string> = {};
@ -94,11 +99,9 @@ class HuiMapCard extends LitElement implements LovelaceCard {
} }
this._config = config; this._config = config;
this._configEntities = ( this._configEntities = config.entities
config.entities ? processConfigEntities<MapEntityConfig>(config.entities)
? processConfigEntities<EntityConfig>(config.entities) : [];
: []
).map((entity) => entity.entity);
} }
public getCardSize(): number { public getCardSize(): number {
@ -238,7 +241,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
this._stateHistory = combinedHistory; this._stateHistory = combinedHistory;
}, },
this._config!.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW, this._config!.hours_to_show! ?? DEFAULT_HOURS_TO_SHOW,
this._configEntities!, (this._configEntities || []).map((entity) => entity.entity)!,
false, false,
false, false,
false false
@ -309,16 +312,14 @@ class HuiMapCard extends LitElement implements LovelaceCard {
( (
states: HassEntities, states: HassEntities,
config: MapCardConfig, config: MapCardConfig,
configEntities?: string[] configEntities?: MapEntityConfig[]
) => { ) => {
if (!states || !config) { if (!states || !config) {
return undefined; return undefined;
} }
let entities = configEntities || []; const geoEntities: string[] = [];
if (config.geo_location_sources) { if (config.geo_location_sources) {
const geoEntities: string[] = [];
// Calculate visible geo location sources // Calculate visible geo location sources
const includesAll = config.geo_location_sources.includes("all"); const includesAll = config.geo_location_sources.includes("all");
for (const stateObj of Object.values(states)) { for (const stateObj of Object.values(states)) {
@ -330,14 +331,21 @@ class HuiMapCard extends LitElement implements LovelaceCard {
geoEntities.push(stateObj.entity_id); geoEntities.push(stateObj.entity_id);
} }
} }
entities = [...entities, ...geoEntities];
} }
return entities.map((entity) => ({ return [
entity_id: entity, ...(configEntities || []).map((entityConf) => ({
color: this._getColor(entity), 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),
})),
];
} }
); );

View File

@ -9,6 +9,7 @@ import {
object, object,
optional, optional,
string, string,
union,
} from "superstruct"; } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { hasLocation } from "../../../../common/entity/has_location"; import { hasLocation } from "../../../../common/entity/has_location";
@ -25,10 +26,19 @@ import { EntityConfig } from "../../entity-rows/types";
import { LovelaceCardEditor } from "../../types"; import { LovelaceCardEditor } from "../../types";
import { processEditorEntities } from "../process-editor-entities"; import { processEditorEntities } from "../process-editor-entities";
import { baseLovelaceCardConfig } from "../structs/base-card-struct"; import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { entitiesConfigStruct } from "../structs/entities-struct";
import { EntitiesEditorEvent } from "../types"; import { EntitiesEditorEvent } from "../types";
import { configElementStyle } from "./config-elements-style"; 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( const cardConfigStruct = assign(
baseLovelaceCardConfig, baseLovelaceCardConfig,
object({ object({
@ -36,7 +46,7 @@ const cardConfigStruct = assign(
aspect_ratio: optional(string()), aspect_ratio: optional(string()),
default_zoom: optional(number()), default_zoom: optional(number()),
dark_mode: optional(boolean()), dark_mode: optional(boolean()),
entities: array(entitiesConfigStruct), entities: array(mapEntitiesConfigStruct),
hours_to_show: optional(number()), hours_to_show: optional(number()),
geo_location_sources: optional(array(string())), geo_location_sources: optional(array(string())),
auto_fit: optional(boolean()), auto_fit: optional(boolean()),