mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 16:38:31 +00:00
feat v1.42.1: room-card font sizes — 3 sliders with a live preview
- space.settings.card_font_scale (tier 2 base) + room.settings name_scale/label_scale (tier 3), 50-300%, multiplied together and stacking with resize-k and kiosk per-screen multipliers - live sample card in both the space and room dialogs - backend schema; smoke_font_scales.mjs (9 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_URL = "/houseplan_files/files"
|
||||||
FILES_DIR = "houseplan/files"
|
FILES_DIR = "houseplan/files"
|
||||||
CONF_ADMIN_ONLY = "admin_only"
|
CONF_ADMIN_ONLY = "admin_only"
|
||||||
VERSION = "1.42.0"
|
VERSION = "1.42.1"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"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",
|
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"version": "1.42.0"
|
"version": "1.42.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ ROOM_SCHEMA = vol.All(
|
|||||||
vol.Optional("fill_mode"): vol.Any(None, vol.In(["none", "lqi", "light", "temp"])),
|
vol.Optional("fill_mode"): vol.Any(None, vol.In(["none", "lqi", "light", "temp"])),
|
||||||
vol.Optional("temp_source"): vol.Any(str, None),
|
vol.Optional("temp_source"): vol.Any(str, None),
|
||||||
vol.Optional("hum_source"): vol.Any(str, None),
|
vol.Optional("hum_source"): vol.Any(str, None),
|
||||||
|
vol.Optional("name_scale"): vol.Any(None, vol.All(vol.Coerce(float), vol.Range(min=0.5, max=3))),
|
||||||
|
vol.Optional("label_scale"): vol.Any(None, vol.All(vol.Coerce(float), vol.Range(min=0.5, max=3))),
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
),
|
),
|
||||||
@@ -104,6 +106,7 @@ SPACE_DISPLAY_SCHEMA = vol.Schema(
|
|||||||
vol.Optional("label_hum"): bool,
|
vol.Optional("label_hum"): bool,
|
||||||
vol.Optional("label_lqi"): bool,
|
vol.Optional("label_lqi"): bool,
|
||||||
vol.Optional("label_light"): bool,
|
vol.Optional("label_light"): bool,
|
||||||
|
vol.Optional("card_font_scale"): vol.All(vol.Coerce(float), vol.Range(min=0.5, max=3)),
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { launch } from './serve.mjs';
|
||||||
|
const { page, browser } = await launch();
|
||||||
|
const res = await page.evaluate(async () => {
|
||||||
|
const out = {};
|
||||||
|
const c = window.__card;
|
||||||
|
const sr = () => c.shadowRoot || c.renderRoot;
|
||||||
|
const spId = c._space;
|
||||||
|
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== spId ? s : ({
|
||||||
|
...s, settings: { ...(s.settings || {}), show_names: true, label_temp: true } })) };
|
||||||
|
c._setMode('view'); c.requestUpdate(); await c.updateComplete;
|
||||||
|
await new Promise((r) => setTimeout(r, 100));
|
||||||
|
const lbl = () => sr().querySelector('.roomlabel');
|
||||||
|
const nameSize = () => parseFloat(getComputedStyle(lbl().querySelector('.rlname')).fontSize);
|
||||||
|
const metaEl = () => lbl().querySelector('.rlmetrics');
|
||||||
|
const n0 = nameSize();
|
||||||
|
const m0 = metaEl() ? parseFloat(getComputedStyle(metaEl()).fontSize) : null;
|
||||||
|
out.baseline = n0 > 0;
|
||||||
|
// 1) пер-комнатный name_scale ×2 — имя удвоилось, подписи нет
|
||||||
|
const room = c._curSpaceCfg.rooms.find((r) => r.name && r.area);
|
||||||
|
room.settings = { ...(room.settings || {}), name_scale: 2 };
|
||||||
|
c.requestUpdate(); await c.updateComplete;
|
||||||
|
const lblR = [...sr().querySelectorAll('.roomlabel')].find((l) => l.textContent.includes(room.name));
|
||||||
|
const n1 = parseFloat(getComputedStyle(lblR.querySelector('.rlname')).fontSize);
|
||||||
|
out.nameDoubled = Math.abs(n1 / n0 - 2) < 0.05;
|
||||||
|
const meta1 = lblR.querySelector('.rlmetrics');
|
||||||
|
out.metaUntouched = meta1 ? Math.abs(parseFloat(getComputedStyle(meta1).fontSize) - m0) < 0.5 : 'no-meta';
|
||||||
|
// 2) label_scale ×2 — подписи удвоились
|
||||||
|
room.settings.label_scale = 2;
|
||||||
|
c.requestUpdate(); await c.updateComplete;
|
||||||
|
const meta2 = lblR.querySelector('.rlmetrics');
|
||||||
|
out.metaDoubled = meta2 && m0 ? Math.abs(parseFloat(getComputedStyle(meta2).fontSize) / m0 - 2) < 0.05 : 'no-meta';
|
||||||
|
// 3) базовый множитель пространства ×1.5 — умножает всё
|
||||||
|
c._serverCfg = { ...c._serverCfg, spaces: c._serverCfg.spaces.map((s) => s.id !== spId ? s : ({
|
||||||
|
...s, settings: { ...(s.settings || {}), card_font_scale: 1.5 } })) };
|
||||||
|
c.requestUpdate(); await c.updateComplete;
|
||||||
|
const lblR2 = [...sr().querySelectorAll('.roomlabel')].find((l) => l.textContent.includes(room.name));
|
||||||
|
const n2 = parseFloat(getComputedStyle(lblR2.querySelector('.rlname')).fontSize);
|
||||||
|
out.spaceMultiplies = Math.abs(n2 / n0 - 3) < 0.1; // 2 × 1.5
|
||||||
|
// 4) диалог комнаты: слайдеры + живой пример реагирует
|
||||||
|
c._setMode('plan'); await c.updateComplete;
|
||||||
|
const model = c._spaceModel().rooms.find((r) => r.id === room.id);
|
||||||
|
c._openRoomEdit(model); await c.updateComplete;
|
||||||
|
out.slidersPrefilled = c._roomNameScale === 2 && c._roomLabelScale === 2;
|
||||||
|
const pv = () => sr().querySelector('.cardpreview .cpname');
|
||||||
|
const p0 = parseFloat(pv().style.fontSize);
|
||||||
|
c._roomNameScale = 1; c.requestUpdate(); await c.updateComplete;
|
||||||
|
const p1 = parseFloat(pv().style.fontSize);
|
||||||
|
out.previewLive = Math.abs(p0 / p1 - 2) < 0.05;
|
||||||
|
c._roomDialogCancel(); await c.updateComplete;
|
||||||
|
// 5) превью есть и в диалоге пространства
|
||||||
|
c._openSpaceDialog('edit', spId); await c.updateComplete;
|
||||||
|
out.spacePreview = !!sr().querySelector('.cardpreview');
|
||||||
|
out.spaceSlider = [...sr().querySelectorAll('.dialog label')].some((l) => l.textContent === c._t('space.card_font'));
|
||||||
|
c._spaceDialog = null; await c.updateComplete;
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
console.log(JSON.stringify(res, null, 1));
|
||||||
|
await browser.close();
|
||||||
File diff suppressed because one or more lines are too long
Vendored
+74
-20
File diff suppressed because one or more lines are too long
@@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.42.1 — 2026-07-26 (room-card font sizes)
|
||||||
|
- Closing the "can't change the font size" feedback: **three sliders**.
|
||||||
|
Space settings gained a base room-card font size for the whole space;
|
||||||
|
Room settings gained independent sizes for the room NAME and the METRICS
|
||||||
|
line (50–300% each). Effects multiply — and stack with the card's corner
|
||||||
|
resize and the kiosk per-screen multiplier as before.
|
||||||
|
- Both dialogs show a **live sample card** that follows the sliders as you
|
||||||
|
drag them.
|
||||||
|
|
||||||
## v1.42.0 — 2026-07-26 (room settings — the third tier)
|
## v1.42.0 — 2026-07-26 (room settings — the third tier)
|
||||||
- **Settings now have four tiers**: global → space → room → device; the more
|
- **Settings now have four tiers**: global → space → room → device; the more
|
||||||
specific tier overrides the more general one (owner's principle, fixed in
|
specific tier overrides the more general one (owner's principle, fixed in
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ Run the *core flows* (marked ★ below) in each environment at least once per mi
|
|||||||
(explicit ripple color still wins); off/white lights unchanged [auto]
|
(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
|
- [ ] 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
|
display mode; clears on 'off'; unavailable never alarms [auto]; reduced-motion static
|
||||||
|
- [ ] Card font scales (v1.42.1): three sliders — space-level base (space
|
||||||
|
dialog) plus per-room name and metrics sizes (room settings), 50–300%,
|
||||||
|
multiplied together and on top of resize-k and kiosk multipliers; the
|
||||||
|
live sample card in both dialogs follows the sliders instantly; name and
|
||||||
|
metrics scale independently [auto]
|
||||||
- [ ] Room settings, tier 3 (v1.42.0): a gear on every room card in the Plan
|
- [ ] Room settings, tier 3 (v1.42.0): a gear on every room card in the Plan
|
||||||
editor opens Room settings (name, HA area incl. the current one, fill
|
editor opens Room settings (name, HA area incl. the current one, fill
|
||||||
override, temp/hum source); the creation dialog has the same section;
|
override, temp/hum source); the creation dialog has the same section;
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.42.0",
|
"version": "1.42.1",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+57
-2
@@ -32,7 +32,7 @@ import './space-card';
|
|||||||
import { cardStyles } from './styles';
|
import { cardStyles } from './styles';
|
||||||
import { langOf, t, type I18nKey } from './i18n';
|
import { langOf, t, type I18nKey } from './i18n';
|
||||||
|
|
||||||
const CARD_VERSION = '1.42.0';
|
const CARD_VERSION = '1.42.1';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
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_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
@@ -163,6 +163,8 @@ class HouseplanCard extends LitElement {
|
|||||||
private _roomHumSrc = '';
|
private _roomHumSrc = '';
|
||||||
private _roomSrcOpen: 'temp' | 'hum' | null = null;
|
private _roomSrcOpen: 'temp' | 'hum' | null = null;
|
||||||
private _roomSrcFilter = '';
|
private _roomSrcFilter = '';
|
||||||
|
private _roomNameScale = 1;
|
||||||
|
private _roomLabelScale = 1;
|
||||||
// plan zoom/pan (zoom is saved per space, locally)
|
// plan zoom/pan (zoom is saved per space, locally)
|
||||||
private _zoom = 1;
|
private _zoom = 1;
|
||||||
private _view: { x: number; y: number; w: number; h: number } | null = null; // current SVG viewBox (vb coordinates)
|
private _view: { x: number; y: number; w: number; h: number } | null = null; // current SVG viewBox (vb coordinates)
|
||||||
@@ -226,6 +228,7 @@ class HouseplanCard extends LitElement {
|
|||||||
tempMin: number;
|
tempMin: number;
|
||||||
tempMax: number;
|
tempMax: number;
|
||||||
showLqi: boolean;
|
showLqi: boolean;
|
||||||
|
cardFontScale: number;
|
||||||
labelTemp: boolean;
|
labelTemp: boolean;
|
||||||
labelHum: boolean;
|
labelHum: boolean;
|
||||||
labelLqi: boolean;
|
labelLqi: boolean;
|
||||||
@@ -301,6 +304,8 @@ class HouseplanCard extends LitElement {
|
|||||||
_roomHumSrc: { state: true },
|
_roomHumSrc: { state: true },
|
||||||
_roomSrcOpen: { state: true },
|
_roomSrcOpen: { state: true },
|
||||||
_roomSrcFilter: { state: true },
|
_roomSrcFilter: { state: true },
|
||||||
|
_roomNameScale: { state: true },
|
||||||
|
_roomLabelScale: { state: true },
|
||||||
_spaceDialog: { state: true },
|
_spaceDialog: { state: true },
|
||||||
_infoCard: { state: true },
|
_infoCard: { state: true },
|
||||||
_rulesDialog: { state: true },
|
_rulesDialog: { state: true },
|
||||||
@@ -2629,6 +2634,7 @@ class HouseplanCard extends LitElement {
|
|||||||
roomColor: disp.color, roomOpacity: disp.opacity, fillMode: disp.fill,
|
roomColor: disp.color, roomOpacity: disp.opacity, fillMode: disp.fill,
|
||||||
tempMin: disp.tempMin, tempMax: disp.tempMax,
|
tempMin: disp.tempMin, tempMax: disp.tempMax,
|
||||||
showLqi: disp.showLqi ?? this._config?.show_signal ?? true,
|
showLqi: disp.showLqi ?? this._config?.show_signal ?? true,
|
||||||
|
cardFontScale: disp.cardFontScale,
|
||||||
labelTemp: disp.labelTemp, labelHum: disp.labelHum,
|
labelTemp: disp.labelTemp, labelHum: disp.labelHum,
|
||||||
labelLqi: disp.labelLqi, labelLight: disp.labelLight,
|
labelLqi: disp.labelLqi, labelLight: disp.labelLight,
|
||||||
cellCm: Number(sp.cell_cm) > 0 ? Number(sp.cell_cm) : 5,
|
cellCm: Number(sp.cell_cm) > 0 ? Number(sp.cell_cm) : 5,
|
||||||
@@ -2642,6 +2648,7 @@ class HouseplanCard extends LitElement {
|
|||||||
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'none',
|
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'none',
|
||||||
tempMin: DEFAULT_TEMP_MIN, tempMax: DEFAULT_TEMP_MAX,
|
tempMin: DEFAULT_TEMP_MIN, tempMax: DEFAULT_TEMP_MAX,
|
||||||
showLqi: this._config?.show_signal ?? true,
|
showLqi: this._config?.show_signal ?? true,
|
||||||
|
cardFontScale: 1,
|
||||||
labelTemp: false, labelHum: false, labelLqi: false, labelLight: false,
|
labelTemp: false, labelHum: false, labelLqi: false, labelLight: false,
|
||||||
cellCm: 5,
|
cellCm: 5,
|
||||||
busy: false,
|
busy: false,
|
||||||
@@ -2727,6 +2734,7 @@ class HouseplanCard extends LitElement {
|
|||||||
temp_min: Number.isFinite(d.tempMin) ? Math.min(d.tempMin, d.tempMax) : DEFAULT_TEMP_MIN,
|
temp_min: Number.isFinite(d.tempMin) ? Math.min(d.tempMin, d.tempMax) : DEFAULT_TEMP_MIN,
|
||||||
temp_max: Number.isFinite(d.tempMax) ? Math.max(d.tempMin, d.tempMax) : DEFAULT_TEMP_MAX,
|
temp_max: Number.isFinite(d.tempMax) ? Math.max(d.tempMin, d.tempMax) : DEFAULT_TEMP_MAX,
|
||||||
show_lqi: d.showLqi,
|
show_lqi: d.showLqi,
|
||||||
|
card_font_scale: d.cardFontScale !== 1 ? d.cardFontScale : undefined,
|
||||||
label_temp: d.labelTemp,
|
label_temp: d.labelTemp,
|
||||||
label_hum: d.labelHum,
|
label_hum: d.labelHum,
|
||||||
label_lqi: d.labelLqi,
|
label_lqi: d.labelLqi,
|
||||||
@@ -2823,6 +2831,7 @@ class HouseplanCard extends LitElement {
|
|||||||
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'none',
|
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'none',
|
||||||
tempMin: DEFAULT_TEMP_MIN, tempMax: DEFAULT_TEMP_MAX,
|
tempMin: DEFAULT_TEMP_MIN, tempMax: DEFAULT_TEMP_MAX,
|
||||||
showLqi: this._config?.show_signal ?? true,
|
showLqi: this._config?.show_signal ?? true,
|
||||||
|
cardFontScale: 1,
|
||||||
labelTemp: false, labelHum: false, labelLqi: false, labelLight: false,
|
labelTemp: false, labelHum: false, labelLqi: false, labelLight: false,
|
||||||
cellCm: 5,
|
cellCm: 5,
|
||||||
busy: false,
|
busy: false,
|
||||||
@@ -3562,6 +3571,8 @@ class HouseplanCard extends LitElement {
|
|||||||
this._roomHumSrc = '';
|
this._roomHumSrc = '';
|
||||||
this._roomSrcOpen = null;
|
this._roomSrcOpen = null;
|
||||||
this._roomSrcFilter = '';
|
this._roomSrcFilter = '';
|
||||||
|
this._roomNameScale = 1;
|
||||||
|
this._roomLabelScale = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Open the room dialog for an EXISTING room (the gear on its card). */
|
/** Open the room dialog for an EXISTING room (the gear on its card). */
|
||||||
@@ -3573,6 +3584,8 @@ class HouseplanCard extends LitElement {
|
|||||||
this._roomFill = (r.settings?.fill_mode as any) || '';
|
this._roomFill = (r.settings?.fill_mode as any) || '';
|
||||||
this._roomTempSrc = r.settings?.temp_source || '';
|
this._roomTempSrc = r.settings?.temp_source || '';
|
||||||
this._roomHumSrc = r.settings?.hum_source || '';
|
this._roomHumSrc = r.settings?.hum_source || '';
|
||||||
|
this._roomNameScale = clampScale(r.settings?.name_scale);
|
||||||
|
this._roomLabelScale = clampScale(r.settings?.label_scale);
|
||||||
this._roomSrcOpen = null;
|
this._roomSrcOpen = null;
|
||||||
this._roomSrcFilter = '';
|
this._roomSrcFilter = '';
|
||||||
this._roomDialog = true;
|
this._roomDialog = true;
|
||||||
@@ -3584,6 +3597,8 @@ class HouseplanCard extends LitElement {
|
|||||||
if (this._roomFill) st.fill_mode = this._roomFill;
|
if (this._roomFill) st.fill_mode = this._roomFill;
|
||||||
if (this._roomTempSrc) st.temp_source = this._roomTempSrc;
|
if (this._roomTempSrc) st.temp_source = this._roomTempSrc;
|
||||||
if (this._roomHumSrc) st.hum_source = this._roomHumSrc;
|
if (this._roomHumSrc) st.hum_source = this._roomHumSrc;
|
||||||
|
if (this._roomNameScale !== 1) st.name_scale = this._roomNameScale;
|
||||||
|
if (this._roomLabelScale !== 1) st.label_scale = this._roomLabelScale;
|
||||||
return Object.keys(st).length ? st : null;
|
return Object.keys(st).length ? st : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3777,7 +3792,7 @@ class HouseplanCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html`<div class="roomlabel ${rows.length ? 'card' : ''}"
|
return html`<div class="roomlabel ${rows.length ? 'card' : ''}"
|
||||||
style="left:${left}%;top:${top}%;color:${disp.color};opacity:${op};--rl-scale:${k}"
|
style="left:${left}%;top:${top}%;color:${disp.color};opacity:${op};--rl-scale:${k};--rl-space:${disp.cardFontScale};--rl-name:${clampScale(r.settings?.name_scale)};--rl-meta:${clampScale(r.settings?.label_scale)}"
|
||||||
@pointerdown=${(e: PointerEvent) => this._labelDown(e, r, space.id)}
|
@pointerdown=${(e: PointerEvent) => this._labelDown(e, r, space.id)}
|
||||||
@pointermove=${(e: PointerEvent) => this._labelMove(e, r, space.id)}
|
@pointermove=${(e: PointerEvent) => this._labelMove(e, r, space.id)}
|
||||||
@pointerup=${() => this._labelUp(r)}
|
@pointerup=${() => this._labelUp(r)}
|
||||||
@@ -4652,6 +4667,13 @@ class HouseplanCard extends LitElement {
|
|||||||
<span>${this._t(k)}</span>
|
<span>${this._t(k)}</span>
|
||||||
</label>`,
|
</label>`,
|
||||||
)}
|
)}
|
||||||
|
<label>${this._t('space.card_font')}</label>
|
||||||
|
<div class="colorrow gsrow">
|
||||||
|
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(d.cardFontScale * 100))}
|
||||||
|
@input=${(e: Event) => (this._spaceDialog = { ...d, cardFontScale: Number((e.target as HTMLInputElement).value) / 100 })} />
|
||||||
|
<span class="opv">${Math.round(d.cardFontScale * 100)}%</span>
|
||||||
|
</div>
|
||||||
|
${this._renderCardPreview(d.cardFontScale, 1, 1)}
|
||||||
<label>${this._t('space.room_color')}</label>
|
<label>${this._t('space.room_color')}</label>
|
||||||
<div class="colorrow">
|
<div class="colorrow">
|
||||||
<input type="color" .value=${d.roomColor}
|
<input type="color" .value=${d.roomColor}
|
||||||
@@ -4739,6 +4761,20 @@ class HouseplanCard extends LitElement {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Live sample of a room card at the given multipliers (dialog preview). */
|
||||||
|
private _renderCardPreview(spaceScale: number, nameScale: number, labelScale: number): TemplateResult {
|
||||||
|
const base = 18 * spaceScale;
|
||||||
|
return html`<div class="cardpreview">
|
||||||
|
<span class="cpname" style="font-size:${(base * nameScale).toFixed(1)}px">
|
||||||
|
${this._t('preview.room_name')}</span>
|
||||||
|
<span class="cpmeta" style="font-size:${(base * 0.62 * labelScale).toFixed(1)}px">
|
||||||
|
<ha-icon icon="mdi:thermometer"></ha-icon>22.4° ·
|
||||||
|
<ha-icon icon="mdi:water-percent"></ha-icon>45% ·
|
||||||
|
<ha-icon icon="mdi:lightbulb-on"></ha-icon>${this._t('roomcard.light_partial', { on: 1, total: 3 })}
|
||||||
|
</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
/** One measurement-source control (average vs an explicit device/entity). */
|
/** One measurement-source control (average vs an explicit device/entity). */
|
||||||
private _renderRoomSource(kind: 'temp' | 'hum'): TemplateResult {
|
private _renderRoomSource(kind: 'temp' | 'hum'): TemplateResult {
|
||||||
const val = kind === 'temp' ? this._roomTempSrc : this._roomHumSrc;
|
const val = kind === 'temp' ? this._roomTempSrc : this._roomHumSrc;
|
||||||
@@ -4828,6 +4864,25 @@ class HouseplanCard extends LitElement {
|
|||||||
)}
|
)}
|
||||||
${this._renderRoomSource('temp')}
|
${this._renderRoomSource('temp')}
|
||||||
${this._renderRoomSource('hum')}
|
${this._renderRoomSource('hum')}
|
||||||
|
|
||||||
|
<label class="dispsection">${this._t('room.sizes_section')}</label>
|
||||||
|
<label>${this._t('room.name_scale')}</label>
|
||||||
|
<div class="colorrow gsrow">
|
||||||
|
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(this._roomNameScale * 100))}
|
||||||
|
@input=${(e: Event) => { this._roomNameScale = Number((e.target as HTMLInputElement).value) / 100; this.requestUpdate(); }} />
|
||||||
|
<span class="opv">${Math.round(this._roomNameScale * 100)}%</span>
|
||||||
|
</div>
|
||||||
|
<label>${this._t('room.label_scale')}</label>
|
||||||
|
<div class="colorrow gsrow">
|
||||||
|
<input type="range" min="50" max="300" step="5" .value=${String(Math.round(this._roomLabelScale * 100))}
|
||||||
|
@input=${(e: Event) => { this._roomLabelScale = Number((e.target as HTMLInputElement).value) / 100; this.requestUpdate(); }} />
|
||||||
|
<span class="opv">${Math.round(this._roomLabelScale * 100)}%</span>
|
||||||
|
</div>
|
||||||
|
${this._renderCardPreview(
|
||||||
|
spaceDisplayOf(this._curSpaceCfg).cardFontScale,
|
||||||
|
this._roomNameScale,
|
||||||
|
this._roomLabelScale,
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<button class="btn ghost" @click=${this._roomDialogCancel}>${this._t('btn.cancel')}</button>
|
<button class="btn ghost" @click=${this._roomDialogCancel}>${this._t('btn.cancel')}</button>
|
||||||
|
|||||||
+6
-1
@@ -317,5 +317,10 @@
|
|||||||
"room.src_average": "Average over the room's sensors (default)",
|
"room.src_average": "Average over the room's sensors (default)",
|
||||||
"room.src_pick": "A specific HA device or entity",
|
"room.src_pick": "A specific HA device or entity",
|
||||||
"room.src_ph": "Choose a source…",
|
"room.src_ph": "Choose a source…",
|
||||||
"toast.room_updated": "Room updated"
|
"toast.room_updated": "Room updated",
|
||||||
|
"space.card_font": "Room-card font size (whole space)",
|
||||||
|
"room.sizes_section": "Font sizes",
|
||||||
|
"room.name_scale": "Room name size",
|
||||||
|
"room.label_scale": "Metrics size",
|
||||||
|
"preview.room_name": "Living room"
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -317,5 +317,10 @@
|
|||||||
"room.src_average": "Средняя по датчикам комнаты (по умолчанию)",
|
"room.src_average": "Средняя по датчикам комнаты (по умолчанию)",
|
||||||
"room.src_pick": "Конкретное устройство или сущность HA",
|
"room.src_pick": "Конкретное устройство или сущность HA",
|
||||||
"room.src_ph": "Выберите источник…",
|
"room.src_ph": "Выберите источник…",
|
||||||
"toast.room_updated": "Комната обновлена"
|
"toast.room_updated": "Комната обновлена",
|
||||||
|
"space.card_font": "Размер шрифта карточек комнат (всё пространство)",
|
||||||
|
"room.sizes_section": "Размеры шрифтов",
|
||||||
|
"room.name_scale": "Размер названия",
|
||||||
|
"room.label_scale": "Размер подписей",
|
||||||
|
"preview.room_name": "Гостиная"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -552,6 +552,8 @@ export interface SpaceDisplay {
|
|||||||
tempMax: number; // comfort range upper bound, °C
|
tempMax: number; // comfort range upper bound, °C
|
||||||
/** Per-space LQI badges near zigbee devices; null = follow the card option. */
|
/** Per-space LQI badges near zigbee devices; null = follow the card option. */
|
||||||
showLqi: boolean | null;
|
showLqi: boolean | null;
|
||||||
|
/** Base font multiplier for room cards (tier 2; rooms multiply on top). */
|
||||||
|
cardFontScale: number;
|
||||||
/** Room-card metrics under the room name (all default off). */
|
/** Room-card metrics under the room name (all default off). */
|
||||||
labelTemp: boolean;
|
labelTemp: boolean;
|
||||||
labelHum: boolean;
|
labelHum: boolean;
|
||||||
@@ -577,6 +579,9 @@ export function spaceDisplayOf(spaceCfg: any): SpaceDisplay {
|
|||||||
tempMin: typeof s.temp_min === 'number' ? s.temp_min : DEFAULT_TEMP_MIN,
|
tempMin: typeof s.temp_min === 'number' ? s.temp_min : DEFAULT_TEMP_MIN,
|
||||||
tempMax: typeof s.temp_max === 'number' ? s.temp_max : DEFAULT_TEMP_MAX,
|
tempMax: typeof s.temp_max === 'number' ? s.temp_max : DEFAULT_TEMP_MAX,
|
||||||
showLqi: typeof s.show_lqi === 'boolean' ? s.show_lqi : null,
|
showLqi: typeof s.show_lqi === 'boolean' ? s.show_lqi : null,
|
||||||
|
cardFontScale: typeof s.card_font_scale === 'number' && s.card_font_scale > 0
|
||||||
|
? Math.min(3, Math.max(0.5, s.card_font_scale))
|
||||||
|
: 1,
|
||||||
labelTemp: s.label_temp === true,
|
labelTemp: s.label_temp === true,
|
||||||
labelHum: s.label_hum === true,
|
labelHum: s.label_hum === true,
|
||||||
labelLqi: s.label_lqi === true,
|
labelLqi: s.label_lqi === true,
|
||||||
|
|||||||
+27
-3
@@ -364,7 +364,7 @@ export const cardStyles = css`
|
|||||||
pointer-events: none; /* draggable only in plan mode (rule below) */
|
pointer-events: none; /* draggable only in plan mode (rule below) */
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
font-size: calc(var(--icon-size, 2.5cqw) * 0.5 * var(--rl-scale, 1) * var(--rl-font, 1));
|
font-size: calc(var(--icon-size, 2.5cqw) * 0.5 * var(--rl-scale, 1) * var(--rl-font, 1) * var(--rl-space, 1));
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -377,7 +377,12 @@ export const cardStyles = css`
|
|||||||
gap: 0.15em;
|
gap: 0.15em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.rlname { display: inline-flex; align-items: center; gap: 0.25em; }
|
.rlname {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25em;
|
||||||
|
font-size: calc(1em * var(--rl-name, 1));
|
||||||
|
}
|
||||||
.rlgear {
|
.rlgear {
|
||||||
--mdc-icon-size: 0.9em;
|
--mdc-icon-size: 0.9em;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -401,7 +406,7 @@ export const cardStyles = css`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.55em;
|
gap: 0.55em;
|
||||||
font-size: 0.62em;
|
font-size: calc(0.62em * var(--rl-meta, 1));
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
@@ -469,6 +474,25 @@ export const cardStyles = css`
|
|||||||
.ctrlstate { display: inline-flex; align-items: center; gap: 5px; color: var(--hp-muted); }
|
.ctrlstate { display: inline-flex; align-items: center; gap: 5px; color: var(--hp-muted); }
|
||||||
.ctrlstate.on { color: var(--hp-txt); }
|
.ctrlstate.on { color: var(--hp-txt); }
|
||||||
.ctrlstate ha-icon { --mdc-icon-size: 15px; }
|
.ctrlstate ha-icon { --mdc-icon-size: 15px; }
|
||||||
|
.cardpreview {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
margin: 8px 0 2px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px dashed var(--hp-muted);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
|
||||||
|
.cardpreview .cpmeta {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3em;
|
||||||
|
font-weight: 600;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
|
||||||
.iconauto {
|
.iconauto {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ export interface RoomCfg {
|
|||||||
/** 'device:<id>' or 'entity:<eid>'; unset = average over the room sensors. */
|
/** 'device:<id>' or 'entity:<eid>'; unset = average over the room sensors. */
|
||||||
temp_source?: string | null;
|
temp_source?: string | null;
|
||||||
hum_source?: string | null;
|
hum_source?: string | null;
|
||||||
|
/** Font multipliers for THIS room's card (0.5-3, unset = 1). */
|
||||||
|
name_scale?: number | null;
|
||||||
|
label_scale?: number | null;
|
||||||
} | null;
|
} | null;
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user