mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
feat v1.1.0: vector floor plans (REMPLANNER SVG, auto-aligned scale/offset) + icon size proportional to plan width (icon_size = % of width, default 2.5)
This commit is contained in:
Executable
+2
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 138 KiB |
Executable
+2
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 76 KiB |
File diff suppressed because one or more lines are too long
Vendored
+62
-61
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.0.1",
|
"version": "1.1.0",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)",
|
"description": "Interactive house plan Lovelace card for Home Assistant (dacha Kirillovskoe)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+2
-2
@@ -16,7 +16,7 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ name: 'icon_size', selector: { number: { min: 14, max: 48, mode: 'box' } } },
|
{ name: 'icon_size', selector: { number: { min: 1, max: 6, step: 0.1, mode: 'box' } } },
|
||||||
{ name: 'show_temperature', selector: { boolean: {} } },
|
{ name: 'show_temperature', selector: { boolean: {} } },
|
||||||
{ name: 'live_states', selector: { boolean: {} } },
|
{ name: 'live_states', selector: { boolean: {} } },
|
||||||
];
|
];
|
||||||
@@ -24,7 +24,7 @@ const SCHEMA = [
|
|||||||
const LABELS: Record<string, string> = {
|
const LABELS: Record<string, string> = {
|
||||||
title: 'Заголовок',
|
title: 'Заголовок',
|
||||||
default_floor: 'Этаж по умолчанию',
|
default_floor: 'Этаж по умолчанию',
|
||||||
icon_size: 'Размер иконок, px',
|
icon_size: 'Размер иконок, % ширины плана',
|
||||||
show_temperature: 'Показывать температуру',
|
show_temperature: 'Показывать температуру',
|
||||||
live_states: 'Живые состояния (вкл/выкл, открыто…)',
|
live_states: 'Живые состояния (вкл/выкл, открыто…)',
|
||||||
};
|
};
|
||||||
|
|||||||
+23
-29
@@ -4,12 +4,12 @@
|
|||||||
* через WS-команды интеграции `houseplan` (fallback — localStorage).
|
* через WS-команды интеграции `houseplan` (fallback — localStorage).
|
||||||
*/
|
*/
|
||||||
import { LitElement, html, svg, css, nothing, TemplateResult, PropertyValues } from 'lit';
|
import { LitElement, html, svg, css, nothing, TemplateResult, PropertyValues } from 'lit';
|
||||||
import { ROOMS, FLOOR_VB, FLOOR_TITLES, AREA_NAMES, IMG_W, IMG_H, Room } from './data/house';
|
import { ROOMS, FLOOR_VB, FLOOR_TITLES, AREA_NAMES, Room } from './data/house';
|
||||||
import { FLOOR_BG } from './data/backgrounds';
|
import { FLOOR_BG, FLOOR_BG_RECT } from './data/backgrounds';
|
||||||
import { EXCLUDED_DOMAINS, GROUP_TITLES, iconFor, DOMAIN_PRIORITY } from './rules';
|
import { EXCLUDED_DOMAINS, GROUP_TITLES, iconFor, DOMAIN_PRIORITY } from './rules';
|
||||||
import './editor';
|
import './editor';
|
||||||
|
|
||||||
const CARD_VERSION = '1.0.1';
|
const CARD_VERSION = '1.1.0';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
|
|
||||||
interface DevItem {
|
interface DevItem {
|
||||||
@@ -100,7 +100,7 @@ class HouseplanCard extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(config: CardConfig): void {
|
public setConfig(config: CardConfig): void {
|
||||||
this._config = { icon_size: 22, show_temperature: true, live_states: true, ...config };
|
this._config = { icon_size: 2.5, show_temperature: true, live_states: true, ...config };
|
||||||
if (config.default_floor) this._floor = config.default_floor;
|
if (config.default_floor) this._floor = config.default_floor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,9 +463,12 @@ class HouseplanCard extends LitElement {
|
|||||||
if (!this._config || !this.hass) return nothing;
|
if (!this._config || !this.hass) return nothing;
|
||||||
const vb = FLOOR_VB[this._floor];
|
const vb = FLOOR_VB[this._floor];
|
||||||
const bg = FLOOR_BG[this._floor];
|
const bg = FLOOR_BG[this._floor];
|
||||||
|
const bgRect = FLOOR_BG_RECT[this._floor];
|
||||||
const rooms = this._rooms.filter((r) => r.floor === this._floor && r.area);
|
const rooms = this._rooms.filter((r) => r.floor === this._floor && r.area);
|
||||||
const devs = this._devices.filter((d) => d.floor === this._floor);
|
const devs = this._devices.filter((d) => d.floor === this._floor);
|
||||||
const iconSize = this._config.icon_size || 22;
|
// размер иконки в % от ширины видимой области плана (legacy px-значения > 8 игнорируются)
|
||||||
|
const cfgSize = this._config.icon_size ?? 2.5;
|
||||||
|
const iconPct = cfgSize > 8 ? 2.5 : cfgSize;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card>
|
||||||
@@ -499,8 +502,8 @@ class HouseplanCard extends LitElement {
|
|||||||
|
|
||||||
<div class="stage ${this._edit ? 'edit' : ''}" style="aspect-ratio:${vb[2]}/${vb[3]}">
|
<div class="stage ${this._edit ? 'edit' : ''}" style="aspect-ratio:${vb[2]}/${vb[3]}">
|
||||||
<svg viewBox="${vb.join(' ')}" preserveAspectRatio="xMidYMid meet">
|
<svg viewBox="${vb.join(' ')}" preserveAspectRatio="xMidYMid meet">
|
||||||
${bg
|
${bg && bgRect
|
||||||
? svg`<image href="${bg}" x="0" y="0" width="${IMG_W}" height="${IMG_H}" preserveAspectRatio="none" />`
|
? svg`<image href="${bg}" x="${bgRect[0]}" y="${bgRect[1]}" width="${bgRect[2]}" height="${bgRect[3]}" preserveAspectRatio="none" />`
|
||||||
: nothing}
|
: nothing}
|
||||||
${rooms.map(
|
${rooms.map(
|
||||||
(r) => svg`<rect
|
(r) => svg`<rect
|
||||||
@@ -513,7 +516,7 @@ class HouseplanCard extends LitElement {
|
|||||||
${!bg ? svg`<text class="rlabel" x="${r.x + r.w / 2}" y="${r.y + 26}">${r.name}</text>` : nothing}`,
|
${!bg ? svg`<text class="rlabel" x="${r.x + r.w / 2}" y="${r.y + 26}">${r.name}</text>` : nothing}`,
|
||||||
)}
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
<div class="devlayer" style="--icon-size:${iconSize}px">
|
<div class="devlayer" style="--icon-size:${iconPct}cqw">
|
||||||
${devs.map((d) => this._renderDevice(d, vb))}
|
${devs.map((d) => this._renderDevice(d, vb))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -693,6 +696,7 @@ class HouseplanCard extends LitElement {
|
|||||||
.stage {
|
.stage {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
container-type: inline-size; /* cqw = % ширины плана для размеров иконок */
|
||||||
}
|
}
|
||||||
.stage svg {
|
.stage svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -740,10 +744,10 @@ class HouseplanCard extends LitElement {
|
|||||||
}
|
}
|
||||||
.dev {
|
.dev {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: var(--icon-size, 22px);
|
width: var(--icon-size, 2.5cqw);
|
||||||
height: var(--icon-size, 22px);
|
height: var(--icon-size, 2.5cqw);
|
||||||
margin: calc(var(--icon-size, 22px) / -2) 0 0 calc(var(--icon-size, 22px) / -2);
|
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
|
||||||
border-radius: 5px;
|
border-radius: 22%;
|
||||||
background: var(--hp-bg);
|
background: var(--hp-bg);
|
||||||
border: 1px solid var(--hp-line);
|
border: 1px solid var(--hp-line);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -757,7 +761,7 @@ class HouseplanCard extends LitElement {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.dev ha-icon {
|
.dev ha-icon {
|
||||||
--mdc-icon-size: calc(var(--icon-size, 22px) * 0.62);
|
--mdc-icon-size: calc(var(--icon-size, 2.5cqw) * 0.62);
|
||||||
}
|
}
|
||||||
.dev:hover {
|
.dev:hover {
|
||||||
background: var(--hp-accent);
|
background: var(--hp-accent);
|
||||||
@@ -791,14 +795,14 @@ class HouseplanCard extends LitElement {
|
|||||||
left: 100%;
|
left: 100%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
margin-left: 2px;
|
margin-left: calc(var(--icon-size, 2.5cqw) * 0.1);
|
||||||
background: rgba(4, 18, 31, 0.9);
|
background: rgba(4, 18, 31, 0.9);
|
||||||
border: 1px solid var(--hp-accent);
|
border: 1px solid var(--hp-accent);
|
||||||
border-radius: 4px;
|
border-radius: calc(var(--icon-size, 2.5cqw) * 0.18);
|
||||||
padding: 0 3px;
|
padding: 0 calc(var(--icon-size, 2.5cqw) * 0.14);
|
||||||
font-size: 10px;
|
font-size: calc(var(--icon-size, 2.5cqw) * 0.45);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 15px;
|
line-height: calc(var(--icon-size, 2.5cqw) * 0.68);
|
||||||
color: #dff1ff;
|
color: #dff1ff;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -921,14 +925,4 @@ if (!customElements.get('houseplan-card')) {
|
|||||||
customElements.define('houseplan-card', HouseplanCard);
|
customElements.define('houseplan-card', HouseplanCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any).customCards = (window as any).customCards || [];
|
(wi
|
||||||
if (!(window as any).customCards.find((c: any) => c.type === 'houseplan-card')) {
|
|
||||||
(window as any).customCards.push({
|
|
||||||
type: 'houseplan-card',
|
|
||||||
name: 'House Plan Card',
|
|
||||||
description: 'Интерактивный план дома: этажи, комнаты, устройства с живыми состояниями и drag-раскладкой.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.info(`%c HOUSEPLAN-CARD %c v${CARD_VERSION} `, 'background:#3ea6ff;color:#04121f;font-weight:700', '')
|
|
||||||
Reference in New Issue
Block a user