mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
feat v1.36.2: per-source glow radius
- marker.glow_radius_cm overrides the global default per device (field in the marker dialog, HA units, placeholder = global default); pools and door sectors use the per-source radius - backend schema; smoke_glow extended (11 checks); docs same-commit
This commit is contained in:
@@ -11,7 +11,7 @@ PLANS_DIR = "houseplan/plans" # relative to the HA configuration directory
|
||||
FILES_URL = "/houseplan_files/files"
|
||||
FILES_DIR = "houseplan/files"
|
||||
CONF_ADMIN_ONLY = "admin_only"
|
||||
VERSION = "1.36.1"
|
||||
VERSION = "1.36.2"
|
||||
|
||||
DEFAULT_CONFIG: dict = {
|
||||
"spaces": [],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,5 +16,5 @@
|
||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||
"requirements": [],
|
||||
"single_config_entry": true,
|
||||
"version": "1.36.1"
|
||||
"version": "1.36.2"
|
||||
}
|
||||
|
||||
@@ -169,6 +169,7 @@ MARKER_SCHEMA = vol.Schema(
|
||||
vol.Optional("description"): vol.Any(str, None),
|
||||
vol.Optional("tap_action"): vol.Any("info", "more-info", "toggle", None),
|
||||
vol.Optional("controls"): vol.Any([str], None),
|
||||
vol.Optional("glow_radius_cm"): vol.Any(vol.All(vol.Coerce(float), vol.Range(min=10, max=10000)), None),
|
||||
vol.Optional("room_id"): vol.Any(str, None),
|
||||
vol.Optional("display"): vol.Any("badge", "ripple", "icon_ripple", None),
|
||||
vol.Optional("ripple_color"): vol.Any(str, None),
|
||||
|
||||
@@ -73,6 +73,17 @@ const res = await page.evaluate(async () => {
|
||||
c.requestUpdate(); await c.updateComplete;
|
||||
const clips2 = [...sr().querySelectorAll('defs clipPath[id^="hp-glowclip"] path')];
|
||||
out.entranceNoSector = clips2.every((p) => ((p.getAttribute('d') || '').match(/M /g) || []).length === 1);
|
||||
// 7а) персональный радиус источника перекрывает глобальный
|
||||
const litMarkerId = litLight.id;
|
||||
c._serverCfg = { ...c._serverCfg, markers: [
|
||||
...(c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId),
|
||||
{ id: litMarkerId, binding: litLight.bindingKind === 'virtual' ? 'virtual' : litLight.bindingKind + ':' + litLight.bindingRef, glow_radius_cm: 150 },
|
||||
] };
|
||||
c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete;
|
||||
const rOwn = Number(sr().querySelector('.glowlayer circle')?.getAttribute('r'));
|
||||
out.perSourceRadius = Math.abs(rOwn - c._cmToUnits(150)) < 0.5;
|
||||
c._serverCfg = { ...c._serverCfg, markers: (c._serverCfg.markers || []).filter((m) => m.id !== litMarkerId) };
|
||||
c._regSignature = ''; c._maybeRebuildDevices(); c.requestUpdate(); await c.updateComplete;
|
||||
// 7) радиус из настроек: 600 см против 300 см — вдвое больше
|
||||
const r600 = Number(sr().querySelector('.glowlayer circle')?.getAttribute('r'));
|
||||
c._serverCfg = { ...c._serverCfg, settings: { ...(c._serverCfg.settings || {}), glow_radius_cm: 300 } };
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+15
-5
File diff suppressed because one or more lines are too long
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## v1.36.2 — 2026-07-23
|
||||
- **Glow radius is now per source**: every device dialog gained a "Glow
|
||||
radius" field (in your HA units; empty = the global default from general
|
||||
settings, shown as the placeholder). A kettle's night light can glow half
|
||||
a meter while the ceiling lamp floods the room. Door sectors use the same
|
||||
per-source radius.
|
||||
|
||||
## v1.36.1 — 2026-07-23
|
||||
- Fixed tap-toggle "doing nothing" on lamps whose individual `light.*` entity
|
||||
is **hidden in the registry** (the usual setup when lamps are folded into a
|
||||
|
||||
@@ -140,6 +140,9 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
||||
(explicit ripple color still wins); off/white lights unchanged [auto]
|
||||
- [ ] Alarm pulse (v1.27.0): leak/smoke/gas/CO/siren in 'on' pulse a red ring over any
|
||||
display mode; clears on 'off'; unavailable never alarms [auto]; reduced-motion static
|
||||
- [ ] Per-source glow radius (v1.36.2): the device dialog has a "Glow radius"
|
||||
field (HA units; empty = general-settings default shown as placeholder);
|
||||
an override changes that source's pool and door sectors only [auto]
|
||||
- [ ] Hidden-light primary (v1.36.1): a lamp whose light entity is HIDDEN in
|
||||
the registry (folded into a light group) still toggles/reflects the lamp,
|
||||
not its do-not-disturb switch or identify button; visible entities of the
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "houseplan-card",
|
||||
"version": "1.36.1",
|
||||
"version": "1.36.2",
|
||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
+35
-6
@@ -32,7 +32,7 @@ import './space-card';
|
||||
import { cardStyles } from './styles';
|
||||
import { langOf, t, type I18nKey } from './i18n';
|
||||
|
||||
const CARD_VERSION = '1.36.1';
|
||||
const CARD_VERSION = '1.36.2';
|
||||
const LS_KEY = 'houseplan_card_layout_v1';
|
||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||
@@ -168,6 +168,7 @@ class HouseplanCard extends LitElement {
|
||||
tapAction: string; // '' = card default
|
||||
controls: string[]; // entities this icon toggles as a group
|
||||
controlsFilter: string;
|
||||
glowRadius: string; // per-device glow radius in display units; '' = global default
|
||||
model: string;
|
||||
link: string;
|
||||
description: string;
|
||||
@@ -2007,6 +2008,11 @@ class HouseplanCard extends LitElement {
|
||||
tapAction: d.marker?.tap_action || '',
|
||||
controls: [...(d.marker?.controls || [])],
|
||||
controlsFilter: '',
|
||||
glowRadius: Number(d.marker?.glow_radius_cm) > 0
|
||||
? String(this._imperial
|
||||
? Math.round((Number(d.marker!.glow_radius_cm) / 30.48) * 10) / 10
|
||||
: Math.round(Number(d.marker!.glow_radius_cm)) / 100)
|
||||
: '',
|
||||
model: d.model || '',
|
||||
link: d.link || '',
|
||||
description: d.description || '',
|
||||
@@ -2020,7 +2026,7 @@ class HouseplanCard extends LitElement {
|
||||
this._markerDialog = {
|
||||
name: '', binding: 'virtual', bindingFilter: '', icon: '', autoIcon: '',
|
||||
display: 'badge', rippleColor: '', rippleSize: 3, size: 1, angle: 0,
|
||||
tapAction: '', controls: [], controlsFilter: '', model: '',
|
||||
tapAction: '', controls: [], controlsFilter: '', glowRadius: '', model: '',
|
||||
link: '', description: '', pdfs: [], room: '', busy: false,
|
||||
};
|
||||
}
|
||||
@@ -2215,6 +2221,11 @@ class HouseplanCard extends LitElement {
|
||||
angle: dlg.angle ? dlg.angle : null,
|
||||
tap_action: dlg.tapAction || null,
|
||||
controls: dlg.controls.length ? dlg.controls : null,
|
||||
glow_radius_cm: (() => {
|
||||
const v = parseFloat(dlg.glowRadius);
|
||||
if (!Number.isFinite(v) || v <= 0) return null;
|
||||
return Math.round(this._imperial ? v * 30.48 : v * 100);
|
||||
})(),
|
||||
model: dlg.model.trim() || null,
|
||||
link: dlg.link.trim() || null,
|
||||
description: dlg.description.trim() || null,
|
||||
@@ -2658,16 +2669,21 @@ class HouseplanCard extends LitElement {
|
||||
return this.hass?.config?.unit_system?.length === 'mi';
|
||||
}
|
||||
|
||||
private get _glowRadiusPlaceholder(): string {
|
||||
const cm = this._glowRadiusCm;
|
||||
return this._imperial ? String(Math.round((cm / 30.48) * 10) / 10) : String(cm / 100);
|
||||
}
|
||||
|
||||
/** Light pools of the current space: dark house, glowing sources. */
|
||||
private _renderGlowLayer(space: SpaceModel): TemplateResult {
|
||||
const colors = this._fillColors;
|
||||
const R = (this._glowRadiusCm / this._cellCm) * this._gridPitch;
|
||||
const defaultR = (this._glowRadiusCm / this._cellCm) * this._gridPitch;
|
||||
const g = this._gridPitch;
|
||||
const polys = space.rooms
|
||||
.map((r) => ({ r, poly: roomPoly(r) }))
|
||||
.filter((x): x is { r: RoomCfg; poly: number[][] } => !!x.poly);
|
||||
const doors = this._openingsR.filter((o) => o.type === 'door');
|
||||
const spots: { pos: { x: number; y: number }; c: string; alpha: number; clip: string | null }[] = [];
|
||||
const spots: { pos: { x: number; y: number }; c: string; alpha: number; clip: string | null; r: number }[] = [];
|
||||
for (const d of this._devices) {
|
||||
if (d.space !== space.id) continue;
|
||||
const lightEid = d.entities.find(
|
||||
@@ -2676,6 +2692,9 @@ class HouseplanCard extends LitElement {
|
||||
if (!lightEid) continue;
|
||||
const glow = glowColorOf(this.hass.states[lightEid], colors.glow_light.c);
|
||||
if (!glow) continue;
|
||||
// per-source radius (owner's decision v1.36.2): marker override, else global
|
||||
const ownCm = Number(d.marker?.glow_radius_cm);
|
||||
const R = Number.isFinite(ownCm) && ownCm > 0 ? (ownCm / this._cellCm) * this._gridPitch : defaultR;
|
||||
const pos = this._pos(d);
|
||||
// innermost room under the source (islands win — reverse order)
|
||||
const home = [...polys].reverse().find((x) => this._pointInRoom([pos.x, pos.y], x.r));
|
||||
@@ -2696,7 +2715,7 @@ class HouseplanCard extends LitElement {
|
||||
}
|
||||
clip = shapes.join(' ');
|
||||
}
|
||||
spots.push({ pos, c: glow.c, alpha: colors.glow_light.a * glow.bri, clip });
|
||||
spots.push({ pos, c: glow.c, alpha: colors.glow_light.a * glow.bri, clip, r: R });
|
||||
}
|
||||
if (!spots.length) return svg`` as unknown as TemplateResult;
|
||||
return svg`<defs>
|
||||
@@ -2709,7 +2728,7 @@ class HouseplanCard extends LitElement {
|
||||
${sp.clip ? svg`<clipPath id="hp-glowclip-${i}"><path d="${sp.clip}"></path></clipPath>` : nothing}`)}
|
||||
</defs>
|
||||
<g class="glowlayer">
|
||||
${spots.map((sp, i) => svg`<circle cx="${sp.pos.x}" cy="${sp.pos.y}" r="${R}"
|
||||
${spots.map((sp, i) => svg`<circle cx="${sp.pos.x}" cy="${sp.pos.y}" r="${sp.r}"
|
||||
fill="url(#hp-glow-${i})" ${''}
|
||||
clip-path=${sp.clip ? `url(#hp-glowclip-${i})` : nothing}></circle>`)}
|
||||
</g>` as unknown as TemplateResult;
|
||||
@@ -3829,6 +3848,16 @@ class HouseplanCard extends LitElement {
|
||||
</div>`
|
||||
: nothing}
|
||||
|
||||
<label>${this._t('marker.glow_radius_label')}</label>
|
||||
<div class="colorrow">
|
||||
<input class="tempin" type="number" min="0.5" step="0.5"
|
||||
placeholder=${this._glowRadiusPlaceholder}
|
||||
.value=${d.glowRadius}
|
||||
@input=${(e: Event) => (this._markerDialog = { ...d, glowRadius: (e.target as HTMLInputElement).value })} />
|
||||
<span class="opl">${this._imperial ? this._t('gs.unit_ft') : this._t('gs.unit_m')}</span>
|
||||
<span class="opl muted">${this._t('marker.glow_radius_hint')}</span>
|
||||
</div>
|
||||
|
||||
<label>${this._t('marker.icon_label')}</label>
|
||||
${customElements.get('ha-icon-picker')
|
||||
? html`<ha-icon-picker .hass=${this.hass} .value=${d.icon}
|
||||
|
||||
+3
-1
@@ -291,5 +291,7 @@
|
||||
"marker.controls_label": "Controls light sources",
|
||||
"marker.controls_hint": "With tap action “Toggle”, a click flips all bound lights at once (any on → all off). The icon mirrors their state.",
|
||||
"marker.controls_filter": "Search lights and switches…",
|
||||
"info.controls": "Controls"
|
||||
"info.controls": "Controls",
|
||||
"marker.glow_radius_label": "Glow radius (light-sources fill)",
|
||||
"marker.glow_radius_hint": "empty = default from general settings"
|
||||
}
|
||||
|
||||
+3
-1
@@ -291,5 +291,7 @@
|
||||
"marker.controls_label": "Управляет источниками света",
|
||||
"marker.controls_hint": "При действии «Переключить» клик разом переключает все привязанные источники (горит хоть один → выключить все). Значок отражает их состояние.",
|
||||
"marker.controls_filter": "Поиск ламп и выключателей…",
|
||||
"info.controls": "Управляет"
|
||||
"info.controls": "Управляет",
|
||||
"marker.glow_radius_label": "Радиус свечения (заливка «Свет по источникам»)",
|
||||
"marker.glow_radius_hint": "пусто = по умолчанию из общих настроек"
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ export interface Marker {
|
||||
angle?: number | null; // icon rotation, degrees
|
||||
/** Entities this icon toggles as a group (wall switch → its lights). */
|
||||
controls?: string[] | null;
|
||||
/** Per-source glow radius in cm (glow fill); null = the global default. */
|
||||
glow_radius_cm?: number | null;
|
||||
}
|
||||
|
||||
/** A door or window: plan geometry (normalized coords), optionally live via entities. */
|
||||
|
||||
Reference in New Issue
Block a user