+ ${M.map((I,i)=>W`
`)}
+ `}_renderMarkupBar(){return Q`
this._tool="draw"}
title="Добавить комнату: соединяйте точки сетки линиями до замкнутого контура">
@@ -111,9 +117,112 @@ const I=globalThis,M=I.ShadowRoot&&(void 0===I.ShadyCSS||I.ShadyCSS.nativeShadow
Удалить
- ${"draw"===this._tool?p`${this._path.length?"точек: "+this._path.length+" · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой":"кликните точку сетки, чтобы начать контур"}
- ${this._path.length?p`Сброс `:v}`:v}
-
`}_renderSpaceDialog(){const I=this._spaceDialog;return p``}_renderInfoCard(){const I=this._infoCard,i=I.primary?this.hass.states[I.primary]:void 0,M=i?this.hass.formatEntityState?.(i)??i.state:null;return Q``}_renderMarkerDialog(){const I=this._markerDialog,i="virtual"===I.binding,M=this._bindingCandidates(),N=(()=>{if(i)return null;const N=M.find(i=>i.value===I.binding);if(N)return N.label;const[T,D]=I.binding.split(":");return"device"===T?this.hass.devices[D]?.name_by_user||this.hass.devices[D]?.name||D:this.hass.states[D]?.attributes?.friendly_name||D})();return Q``}_renderSpaceDialog(){const I=this._spaceDialog;return Q``}_renderRoomDialog(){const I=this._freeAreas;return p``}_renderRoomDialog(){const I=this._freeAreas;return Q``}_renderEditbar(){const I=this._selId?this._devices.find(I=>I.id===this._selId):null,M=I?this._pos(I):null;return p`
+
`}_renderEditbar(){const I=this._selId?this._devices.find(I=>I.id===this._selId):null,i=I?this._pos(I):null;return Q`
${I?I.name:"Режим правки — тащите иконки мышью"}
- ${I&&M?p`X X this._applyXY("x",I.target.value)} />
- Y Y this._applyXY("y",I.target.value)} />`:v}
${this._serverStorage?this._norm?"конфиг и раскладка: сервер":"раскладка: сервер · конфиг: встроенный":"сохранение: этот браузер"}
- ${this._serverStorage&&!this._norm?p`
${this._migrating?"…":"На сервер"}
`:v}
-
`}}xI.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_edit:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_migrating:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0}},xI.styles=((I,...M)=>{const N=1===I.length?I[0]:M.reduce((M,i,N)=>M+(I=>{if(!0===I._$cssResult$)return I.cssText;if("number"==typeof I)return I;throw Error("Value passed to 'css' function must be a 'css' function result: "+I+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+I[N+1],I[0]);return new T(N,I,i)})`
+
`}}LI.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_edit:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_migrating:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_markerDialog:{state:!0}},LI.styles=((I,...i)=>{const N=1===I.length?I[0]:i.reduce((i,M,N)=>i+(I=>{if(!0===I._$cssResult$)return I.cssText;if("number"==typeof I)return I;throw Error("Value passed to 'css' function must be a 'css' function result: "+I+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(M)+I[N+1],I[0]);return new T(N,I,M)})`
:host {
--hp-bg: var(--card-background-color, #16212e);
--hp-line: var(--divider-color, #2b3d4f);
@@ -537,6 +646,165 @@ const I=globalThis,M=I.ShadowRoot&&(void 0===I.ShadyCSS||I.ShadyCSS.nativeShadow
.dialog .row .spacer {
flex: 1;
}
+ .dialog.wide {
+ width: min(440px, 94vw);
+ }
+ .dialog .body {
+ max-height: 66vh;
+ overflow-y: auto;
+ }
+ .descin {
+ width: 100%;
+ box-sizing: border-box;
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: 6px;
+ padding: 6px 8px;
+ font-size: 13px;
+ font-family: inherit;
+ resize: vertical;
+ }
+ .bindsel {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ border: 1px solid var(--hp-line);
+ border-radius: 8px;
+ padding: 8px;
+ }
+ .bindsel .opt {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ border-radius: 6px;
+ padding: 6px 8px;
+ cursor: pointer;
+ font-size: 12.5px;
+ font-family: inherit;
+ }
+ .bindsel .opt.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .curbind {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 12.5px;
+ color: var(--hp-txt);
+ flex-wrap: wrap;
+ }
+ .curbind .ref {
+ color: var(--hp-muted);
+ font-size: 11px;
+ }
+ .candlist {
+ max-height: 160px;
+ overflow-y: auto;
+ border-top: 1px solid var(--hp-line);
+ }
+ .cand {
+ display: flex;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 6px 8px;
+ cursor: pointer;
+ border-radius: 6px;
+ font-size: 12.5px;
+ }
+ .cand:hover {
+ background: rgba(127, 127, 127, 0.15);
+ }
+ .cand.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .cand .cs {
+ color: var(--hp-muted);
+ font-size: 11px;
+ white-space: nowrap;
+ }
+ .cand.sel .cs {
+ color: var(--text-primary-color, #fff);
+ opacity: 0.85;
+ }
+ .cand.muted {
+ color: var(--hp-muted);
+ cursor: default;
+ }
+ .pdfedit {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ align-items: center;
+ }
+ .pdftag {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ border: 1px solid var(--hp-line);
+ border-radius: 6px;
+ padding: 3px 6px;
+ font-size: 12px;
+ }
+ .pdftag a {
+ color: var(--hp-txt);
+ text-decoration: none;
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .pdftag .x {
+ --mdc-icon-size: 15px;
+ cursor: pointer;
+ color: var(--hp-muted);
+ }
+ .pdftag .x:hover {
+ color: #ff7a5c;
+ }
+ .inforow {
+ display: flex;
+ gap: 10px;
+ font-size: 13px;
+ margin: 3px 0;
+ }
+ .inforow .k {
+ color: var(--hp-muted);
+ min-width: 84px;
+ }
+ .inforow a {
+ color: var(--hp-accent);
+ word-break: break-all;
+ }
+ .infodesc {
+ font-size: 13px;
+ white-space: pre-wrap;
+ margin-top: 6px;
+ }
+ .infodesc.muted {
+ color: var(--hp-muted);
+ }
+ .pdflist {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ }
+ .pdf {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ color: var(--hp-accent);
+ text-decoration: none;
+ }
+ ha-icon-picker {
+ display: block;
+ }
.dialogwrap {
background: rgba(0, 0, 0, 0.45);
display: flex;
@@ -688,4 +956,4 @@ const I=globalThis,M=I.ShadowRoot&&(void 0===I.ShadyCSS||I.ShadyCSS.nativeShadow
z-index: 120;
max-width: 90vw;
}
- `,customElements.get("houseplan-card")||customElements.define("houseplan-card",xI),window.customCards=window.customCards||[],window.customCards.find(I=>"houseplan-card"===I.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Интерактивный план дома: пространства, комнаты, устройства с живыми состояниями и drag-раскладкой."}),console.info("%c HOUSEPLAN-CARD %c v1.5.1 ","background:#3ea6ff;color:#04121f;font-weight:700","");
+ `,customElements.get("houseplan-card")||customElements.define("houseplan-card",LI),window.customCards=window.customCards||[],window.customCards.find(I=>"houseplan-card"===I.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Интерактивный план дома: пространства, комнаты, устройства с живыми состояниями и drag-раскладкой."}),console.info("%c HOUSEPLAN-CARD %c v1.6.1 ","background:#3ea6ff;color:#04121f;font-weight:700","");
diff --git a/custom_components/houseplan/websocket_api.py b/custom_components/houseplan/websocket_api.py
index 1b12a8e..3b9bbf1 100755
--- a/custom_components/houseplan/websocket_api.py
+++ b/custom_components/houseplan/websocket_api.py
@@ -12,7 +12,10 @@ import voluptuous as vol
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
-from .const import CONF_ADMIN_ONLY, DEFAULT_CONFIG, DOMAIN, PLANS_DIR, PLANS_URL
+from .const import (
+ CONF_ADMIN_ONLY, DEFAULT_CONFIG, DOMAIN,
+ FILES_DIR, FILES_URL, PLANS_DIR, PLANS_URL,
+)
POS_SCHEMA = vol.Schema(
{vol.Required("x"): vol.Coerce(float), vol.Required("y"): vol.Coerce(float)},
@@ -66,6 +69,26 @@ VIRTUAL_SCHEMA = vol.Schema(
},
extra=vol.ALLOW_EXTRA,
)
+MARKER_SCHEMA = vol.Schema(
+ {
+ vol.Required("id"): str,
+ # 'device: