v1.52.1: the v1.52.0 review (HP-1520-01/-02, HP-1513-01)

- HP-1520-01: the glow layer is hidden in the plan editor, but the yellow
  suppression still fired there — a lit lamp had NEITHER indicator. The
  gate now equals the layer's visibility (disp.fill === 'glow' &&
  !this._markup), so the badge returns exactly where the spot is absent.
- HP-1513-01: the static card ignored marker.size and marker.angle — the
  same stored marker looked different on the two cards. It mirrors
  --dev-scale and the icon rotation now; geometry only, no live dressing.
- HP-1520-02: TESTING/UX-MODES still demanded the removed RGB icon tint,
  and the lightC comment described the old use. All three brought to the
  v1.52.0 contract.

smoke_light_badges grew the editor-mode vectors; new
smoke_size_angle_parity asserts the x3 ratio inside each card (absolute px
are incomparable across containers) and rotation on both. Inventory:
147 / 51 / 43 / 71.
This commit is contained in:
Matysh
2026-07-29 21:55:13 +03:00
parent fa59767c69
commit d7c20ff6a5
16 changed files with 163 additions and 35 deletions
+4 -4
View File
@@ -1583,8 +1583,8 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
z-index: 120;
max-width: 90vw;
}
`,Gi=1e3;function Vi(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}function Ki(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function Zi(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Gi,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Gi,i[1]*e,i[2]*Gi,i[3]*e],bg:t.plan_url?{href:bi(t.plan_url),...Vi(t.plan_aspect,Gi)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Ki(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Gi:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Gi:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Gi,t[1]*e]):void 0}})}}):[]}function Ji(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Yi(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}function Xi(t){const e=Zi(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=i.vb,o=ti(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,r=n>8?2.5:n,a={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(a[t.area]=e.id);const l=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):ht,c=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),h=Oi({hass:t.hass,areaToSpace:a,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:l,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>Bi(t.lang,e),iconRules:c}),d=h.filter(e=>e.space===t.spaceId),p=d.filter(t=>!t.hidden),u=function(t,e,i){const s={},o=i/100*Gi*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=Ji(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:n.x+r+h*(e%c+.5),y:n.y+r+d*(Math.floor(e/c)+.5)}));je(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=p[e])}return s}(d,i,r),_=i.rooms.filter(t=>t.area||o.showBorders).map(e=>{let s="room "+(i.bg?"overlay":"yard"),n="";const r=vi(o.fill,e);if(o.showBorders||"none"!==r){s+=" styled";const i=[`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`],a=e.area?ni(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Ti(t,o.entities);null!=e&&s.push(e)}return He(s)}(t.hass,d,e.area):null,"light"===r?Ii(t.hass,d,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=zi(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,d,e.area):null,o.tempMin,o.tempMax,si(t.cfg?.settings)):null;a?(s+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!o.showNames,l=Yi(e),c=e.poly?j`<polygon class="${s}" style="${n}" points="${e.poly.map(t=>t.join(",")).join(" ")}"></polygon>`:j`<rect class="${s}" style="${n}" x="${e.x}" y="${e.y}" width="${e.w}" height="${e.h}" rx="${.03*Math.min(e.w,e.h)}"></rect>`;return j`${c}${a?j`<text class="rlabel" x="${l[0]}" y="${l[1]}">${e.name}</text>`:G}`}),m=p.map(e=>{const o=function(t,e,i,s,o){const n=e[t.id];if(n&&n.s===t.space)return{x:n.x*Gi,y:n.y*Gi};if(s[t.id])return s[t.id];const r=o.vb;return{x:r[0]+r[2]/2,y:r[1]+r[3]/2}}(e,t.layout,t.cfg,u,i),n=(o.x-s[0])/s[2]*100,r=(o.y-s[1])/s[3]*100;return B`<div class="dev ${e.virtual?"virtual":""}" style="left:${n}%;top:${r}%">
<ha-icon icon="${e.icon}"></ha-icon>
`,Gi=1e3;function Vi(t,e){const i=Number(t),s=Number.isFinite(i)&&i>0?i:1,o=s>=1?e:e*s,n=s>=1?e/s:e;return{x:(e-o)/2,y:(e-n)/2,w:o,h:n}}function Ki(t){if(null==t.x||null==t.y)return{x:t.x,y:t.y,w:t.w,h:t.h};const e=Number(t.w)||0,i=Number(t.h)||0;return{x:e<0?t.x+e:t.x,y:i<0?t.y+i:t.y,w:Math.abs(e),h:Math.abs(i)}}function Zi(t){return t&&Array.isArray(t.spaces)?t.spaces.map(t=>{const e=Gi,i=function(t){return Array.isArray(t)&&4===t.length&&t.every(t=>Number.isFinite(t))&&t[2]>1e-6&&t[3]>1e-6?t:[0,0,1,1]}(t.view_box);return{id:t.id,title:t.title,vb:[i[0]*Gi,i[1]*e,i[2]*Gi,i[3]*e],bg:t.plan_url?{href:bi(t.plan_url),...Vi(t.plan_aspect,Gi)}:null,rooms:(t.rooms||[]).map(t=>{const i={...t,...Ki(t)};return{id:i.id,name:i.name,area:i.area??null,open_to:i.open_to||void 0,settings:i.settings||void 0,x:null!=i.x?i.x*Gi:void 0,y:null!=i.y?i.y*e:void 0,w:null!=i.w?i.w*Gi:void 0,h:null!=i.h?i.h*e:void 0,poly:i.poly?i.poly.map(t=>[t[0]*Gi,t[1]*e]):void 0}})}}):[]}function Ji(t){if(t.poly&&t.poly.length){const e=t.poly.map(t=>t[0]),i=t.poly.map(t=>t[1]),s=Math.min(...e),o=Math.min(...i);return{x:s,y:o,w:Math.max(...e)-s,h:Math.max(...i)-o}}return{x:t.x??0,y:t.y??0,w:t.w??0,h:t.h??0}}function Yi(t){if(t.poly){const e=t.poly.length;return[t.poly.reduce((t,e)=>t+e[0],0)/e,t.poly.reduce((t,e)=>t+e[1],0)/e]}return[t.x+t.w/2,t.y+.1*Math.min(t.w,t.h)]}function Xi(t){const e=Zi(t.cfg),i=e.find(e=>e.id===t.spaceId);if(!i)return null;const s=i.vb,o=ti(t.cfg.spaces.find(e=>e.id===t.spaceId)),n=t.iconSize??2.5,r=n>8?2.5:n,a={};for(const e of t.cfg.spaces||[])for(const t of e.rooms||[])t.area&&(a[t.area]=e.id);const l=t.cfg.settings?.exclude_integrations?new Set(t.cfg.settings.exclude_integrations):ht,c=pt(t.cfg.settings?.icon_rules?.length?t.cfg.settings.icon_rules:dt),h=Oi({hass:t.hass,areaToSpace:a,markers:t.cfg.markers||[],settings:t.cfg.settings||{},excluded:l,showAll:!!t.cfg.settings?.show_all,firstSpaceId:e[0]?.id||"",loc:e=>Bi(t.lang,e),iconRules:c}),d=h.filter(e=>e.space===t.spaceId),p=d.filter(t=>!t.hidden),u=function(t,e,i){const s={},o=i/100*Gi*1.3;for(const i of e.rooms){if(!i.area)continue;const e=t.filter(t=>t.area===i.area);if(!e.length)continue;const n=Ji(i),r=.1*Math.min(n.w,n.h),a=n.w-2*r,l=n.h-2*r,c=Math.max(1,Math.round(Math.sqrt(e.length*a/Math.max(l,1)))),h=a/c,d=l/Math.max(Math.ceil(e.length/c),1),p=e.map((t,e)=>({x:n.x+r+h*(e%c+.5),y:n.y+r+d*(Math.floor(e/c)+.5)}));je(p,n,o,.5*r),e.forEach((t,e)=>s[t.id]=p[e])}return s}(d,i,r),_=i.rooms.filter(t=>t.area||o.showBorders).map(e=>{let s="room "+(i.bg?"overlay":"yard"),n="";const r=vi(o.fill,e);if(o.showBorders||"none"!==r){s+=" styled";const i=[`--room-stroke:${o.color}`,`--room-stroke-op:${o.showBorders?o.opacity:0}`],a=e.area?ni(r,"lqi"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i||o.virtual)continue;const e=Ti(t,o.entities);null!=e&&s.push(e)}return He(s)}(t.hass,d,e.area):null,"light"===r?Ii(t.hass,d,e.area):"none","temp"===r?function(t,e,i){const s=[];for(const o of e){if(o.area!==i)continue;if("mdi:thermometer"!==o.icon&&"mdi:air-filter"!==o.icon)continue;const e=zi(t,o.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}(t.hass,d,e.area):null,o.tempMin,o.tempMax,si(t.cfg?.settings)):null;a?(s+=" filled",i.push(`--room-fill:${a.c}`,`--room-fill-op:${a.a.toFixed(3)}`)):i.push("--room-fill:transparent","--room-fill-op:0"),n=i.join(";")}const a=!i.bg&&!o.showNames,l=Yi(e),c=e.poly?j`<polygon class="${s}" style="${n}" points="${e.poly.map(t=>t.join(",")).join(" ")}"></polygon>`:j`<rect class="${s}" style="${n}" x="${e.x}" y="${e.y}" width="${e.w}" height="${e.h}" rx="${.03*Math.min(e.w,e.h)}"></rect>`;return j`${c}${a?j`<text class="rlabel" x="${l[0]}" y="${l[1]}">${e.name}</text>`:G}`}),m=p.map(e=>{const o=function(t,e,i,s,o){const n=e[t.id];if(n&&n.s===t.space)return{x:n.x*Gi,y:n.y*Gi};if(s[t.id])return s[t.id];const r=o.vb;return{x:r[0]+r[2]/2,y:r[1]+r[3]/2}}(e,t.layout,t.cfg,u,i),n=(o.x-s[0])/s[2]*100,r=(o.y-s[1])/s[3]*100,a=Number(e.marker?.size)>0?Number(e.marker.size):1,l=Number(e.marker?.angle)||0,c=[`left:${n}%`,`top:${r}%`];return 1!==a&&c.push(`--dev-scale:${a}`),B`<div class="dev ${e.virtual?"virtual":""}" style="${c.join(";")}">
<ha-icon icon="${e.icon}" style=${l?`transform:rotate(${l}deg)`:G}></ha-icon>
</div>`}),g=o.showNames?i.rooms.filter(t=>t.name).map(e=>{const n=function(t,e,i){const s=i["rl_"+(t.id||"")];if(s&&s.s===e)return{x:s.x*Gi,y:s.y*Gi};const o=Yi(t);return{x:o[0],y:o[1]}}(e,i.id,t.layout,t.cfg),r=(n.x-s[0])/s[2]*100,a=(n.y-s[1])/s[3]*100,l=Math.min(1,o.opacity+.25);return B`<div class="roomlabel" style="left:${r}%;top:${a}%;color:${o.color};opacity:${l}">${e.name}</div>`}):[],f=i.bg?t.displayUrl?t.displayUrl(i.bg.href):i.bg.href:"";return B`
<div class="hp-static-stage" style="aspect-ratio:${s[2]}/${s[3]}">
<svg viewBox="${s[0]} ${s[1]} ${s[2]} ${s[3]}" preserveAspectRatio="xMidYMid meet">
@@ -1970,7 +1970,7 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
${this._renderOpenings(n)}
</svg>
<div class="devlayer" style="--icon-size:${(l*i[2]*(this._kiosk?this._kioskScale.icon:1)/c.w).toFixed(3)}cqw;--rl-font:${this._kiosk?this._kioskScale.font:1}">
${o.map(t=>this._renderDevice(t,c,r,"glow"===n.fill))}
${o.map(t=>this._renderDevice(t,c,r,"glow"===n.fill&&!this._markup))}
${this._renderOpeningLocks(c)}
${n.showNames||this._markup?e.rooms.map(t=>this._renderRoomLabel(t,e,c,n)):G}
${this._markup?e.rooms.map(t=>this._renderRoomGear(t,e,c)):G}
@@ -2668,4 +2668,4 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow
</button>`}
</div>
</div>
</div>`}}fs.properties={_hdrH:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},fs.ZOOM_MAX=8,fs.ZOOM_MIN=.4,fs._touchSeen=!1,fs._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,fs.styles=Wi,customElements.get("houseplan-card")||customElements.define("houseplan-card",fs),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.52.0 ","background:#3ea6ff;color:#04121f;font-weight:700","");
</div>`}}fs.properties={_hdrH:{state:!0},hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_mode:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_mergeSel:{state:!0},_openingDialog:{state:!0},_openingInfo:{state:!0},_mergeDialog:{state:!0},_splitSel:{state:!0},_decorTool:{state:!0},_decorStyle:{state:!0},_decorDraft:{state:!0},_decorSel:{state:!0},_decorTextDialog:{state:!0},_kioskDialog:{state:!0},_kioskDots:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_roomEditId:{state:!0},_roomFill:{state:!0},_roomTempSrc:{state:!0},_roomHumSrc:{state:!0},_roomSrcOpen:{state:!0},_roomSrcFilter:{state:!0},_roomNameScale:{state:!0},_roomLabelScale:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_rulesDialog:{state:!0},_settingsDialog:{state:!0},_importDialog:{state:!0},_markerDialog:{state:!0},_zoom:{state:!0},_view:{state:!0}},fs.ZOOM_MAX=8,fs.ZOOM_MIN=.4,fs._touchSeen=!1,fs._noHoverMq="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(hover: none)").matches,fs.styles=Wi,customElements.get("houseplan-card")||customElements.define("houseplan-card",fs),window.customCards=window.customCards||[],window.customCards.find(t=>"houseplan-card"===t.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Interactive house plan: spaces, rooms and devices with live states and drag layout."}),console.info("%c HOUSEPLAN-CARD %c v1.52.1 ","background:#3ea6ff;color:#04121f;font-weight:700","");