From d2faa070b58751ccb7232cb1faf27ab9ad4b889f Mon Sep 17 00:00:00 2001 From: Matysh Date: Fri, 31 Jul 2026 09:46:46 +0300 Subject: [PATCH] No heading arrow on the puck (owner call) The wedge looked like clutter at badge size; the trail already shows where the robot is going. Smoke asserts its absence now. --- .../houseplan/frontend/houseplan-card.js | 20 ++----------------- demo/smoke_vacuum.mjs | 9 +++------ demo/srv/assets/houseplan-card.js | 20 ++----------------- dist/houseplan-card.js | 20 ++----------------- docs/TESTING.md | 4 ++-- docs/VACUUM.md | 6 ++---- src/houseplan-card.ts | 5 ----- src/styles.ts | 15 -------------- 8 files changed, 13 insertions(+), 86 deletions(-) diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js index d9ec1c9..a48c3cf 100755 --- a/custom_components/houseplan/frontend/houseplan-card.js +++ b/custom_components/houseplan/frontend/houseplan-card.js @@ -1263,21 +1263,6 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .vacpuck.jump { transition: none; } .vacpuck.stale { opacity: 0.45; animation: none; } .vacpuck ha-icon { --mdc-icon-size: calc(var(--puck-size) * 0.68); color: var(--hp-txt); } - .vacwedge { - position: absolute; - inset: 0; - pointer-events: none; - } - .vacwedge::before { - content: ''; - position: absolute; - top: -5px; - left: 50%; - margin-left: -3.5px; - border-left: 3.5px solid transparent; - border-right: 3.5px solid transparent; - border-bottom: 6px solid var(--hp-accent); - } @keyframes vacpulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hp-accent) 45%, transparent); } 70% { box-shadow: 0 0 0 12px transparent; } @@ -2133,13 +2118,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?B`
${ti(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} - `}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=null!=n.pos.a?n.pos.a+180*Math.atan2(r[3],r[0])/Math.PI:null,g=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
`}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
{t.stopPropagation();const e=this._vacEntity(o);e&&this._openMoreInfo(e)}}> - ${null!=m?B`
`:G} - +
`)}return i.length||s.length?B` ${s.length?j`${s}`:G} ${i}`:G}_renderDevice(t,e,i=!0,s=!1){const o=this._pos(t),n=(o.x-e.x)/e.w*100,r=(o.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Fi(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ui(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=t.primary?this.hass.states[t.primary]:void 0,m="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":_&&!isNaN(parseFloat(_.state))?parseFloat(_.state)+(_.attributes?.unit_of_measurement?" "+_.attributes.unit_of_measurement:""):null,g=t.primary?t.primary.split(".")[0]:null,f=this._config?.live_states&&!t.hidden?ci(t.icon,g,_?.attributes?.device_class,_?.state,!!d?.icon):t.icon,v=(d?.controls||[]).filter(_i),b=this._config?.live_states&&!t.hidden?v.length?v.map(t=>hi(this.hass.states[t])).find(t=>t)||null:"light"===g?hi(_):null:null,y=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Si.has(e))}(g,_?.attributes?.device_class,_?.state),w=u&&!t.hidden&&!!t.primary&&$e(this.hass.states[t.primary]?.state),x=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,$=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${n}%`,`top:${r}%`];return 1!==x&&S.push(`--dev-scale:${x}`),u&&(S.push(`--ripple-scale:${$}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):b&&S.push(`--ripple-color:${b}`)),B`
{ && pcs.borderTopColor === dcs.borderTopColor; o.puck80pct = Math.abs(puck.getBoundingClientRect().width - devEl.getBoundingClientRect().width * 0.8) < 2; - o.puckHasWedge = !!sr().querySelector('.vacwedge'); + o.noWedge = !sr().querySelector('.vacwedge'); // owner 2026-07-31: no heading arrow o.baseStaysDuringCleaning = !!sr().querySelector('.dev'); // drive: two more points -> trail polyline grows; puck moves @@ -54,9 +54,6 @@ const out = await page.evaluate(async () => { o.trailDrawn = !!trail && trail.getAttribute('points').split(' ').length >= 3; o.trailScaledByMatrix = !!trail && trail.getAttribute('points').startsWith('300.0,400.0'); - // heading wedge rotates by a + matrix rotation (rotation=0 here) - const wedge = sr().querySelector('.vacwedge'); - o.wedgeRotated = wedge && wedge.style.transform.includes('45'); // trail off via marker option cfg.markers.find((m) => m.id === 'e_vacuum_robo').vacuum.trail = false; @@ -125,8 +122,8 @@ const out = await page.evaluate(async () => { checkAll(out, { dockedNoPuck: true, baseMarkerThere: true, puckAppears: true, puckRound: true, puckPlateMatchesDev: true, puck80pct: true, - puckHasWedge: true, baseStaysDuringCleaning: true, trailDrawn: true, - trailScaledByMatrix: true, wedgeRotated: true, trailToggleOff: true, + noWedge: true, baseStaysDuringCleaning: true, trailDrawn: true, + trailScaledByMatrix: true, trailToggleOff: true, puckGoneWhenDocked: true, trailLingers: true, hiddenNoPuck: true, unknownMapNoPuck: true, wizardDevFound: true, wizardBanner: true, wizardSavedMatrix: true, diff --git a/demo/srv/assets/houseplan-card.js b/demo/srv/assets/houseplan-card.js index d9ec1c9..a48c3cf 100755 --- a/demo/srv/assets/houseplan-card.js +++ b/demo/srv/assets/houseplan-card.js @@ -1263,21 +1263,6 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow .vacpuck.jump { transition: none; } .vacpuck.stale { opacity: 0.45; animation: none; } .vacpuck ha-icon { --mdc-icon-size: calc(var(--puck-size) * 0.68); color: var(--hp-txt); } - .vacwedge { - position: absolute; - inset: 0; - pointer-events: none; - } - .vacwedge::before { - content: ''; - position: absolute; - top: -5px; - left: 50%; - margin-left: -3.5px; - border-left: 3.5px solid transparent; - border-right: 3.5px solid transparent; - border-bottom: 6px solid var(--hp-accent); - } @keyframes vacpulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--hp-accent) 45%, transparent); } 70% { box-shadow: 0 0 0 12px transparent; } @@ -2133,13 +2118,12 @@ const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow ${a.length?B`
${ti(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} -
`}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=null!=n.pos.a?n.pos.a+180*Math.atan2(r[3],r[0])/Math.PI:null,g=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
`}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
{t.stopPropagation();const e=this._vacEntity(o);e&&this._openMoreInfo(e)}}> - ${null!=m?B`
`:G} - +
`)}return i.length||s.length?B` ${s.length?j`${s}`:G} ${i}`:G}_renderDevice(t,e,i=!0,s=!1){const o=this._pos(t),n=(o.x-e.x)/e.w*100,r=(o.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Fi(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ui(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=t.primary?this.hass.states[t.primary]:void 0,m="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":_&&!isNaN(parseFloat(_.state))?parseFloat(_.state)+(_.attributes?.unit_of_measurement?" "+_.attributes.unit_of_measurement:""):null,g=t.primary?t.primary.split(".")[0]:null,f=this._config?.live_states&&!t.hidden?ci(t.icon,g,_?.attributes?.device_class,_?.state,!!d?.icon):t.icon,v=(d?.controls||[]).filter(_i),b=this._config?.live_states&&!t.hidden?v.length?v.map(t=>hi(this.hass.states[t])).find(t=>t)||null:"light"===g?hi(_):null:null,y=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Si.has(e))}(g,_?.attributes?.device_class,_?.state),w=u&&!t.hidden&&!!t.primary&&$e(this.hass.states[t.primary]?.state),x=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,$=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${n}%`,`top:${r}%`];return 1!==x&&S.push(`--dev-scale:${x}`),u&&(S.push(`--ripple-scale:${$}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):b&&S.push(`--ripple-color:${b}`)),B`
${a.length?B`
${ti(this._t("vac.cal_maps"),{maps:a.join(", ")})}
`:G} `:G} -
`}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=null!=n.pos.a?n.pos.a+180*Math.atan2(r[3],r[0])/Math.PI:null,g=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
`}_vacMapId(t,e){if("default"!==e.mapId)return e.mapId;const i=this._vacEntity(t),s=i?this.hass?.states[i]?.attributes?.selected_map:null;return s?String(s):"default"}_vacSaveMatrix(t,e,i,s){const o=this._serverCfg,n=o?.markers?.find(e=>e.id===t);if(!n)return;const r={...n.vacuum||{}};r.source=e,r.calibration={...r.calibration||{},[i]:s.map(t=>Number(t.toFixed(6)))},n.vacuum=r,this._regSignature="",this._saveConfig(),this.requestUpdate()}_vacAutoCalibrate(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;if(!e||!i||i.rooms.length<3)return void this._showToast(this._t("vac.autocal_no_rooms"));const s=this._spaceModel(t.space),o=(s?.rooms||[]).filter(t=>t.name&&t.poly?.length>=3).map(t=>{const e=Ae(t.poly);return{name:t.name,cx:e[0],cy:e[1]}}),n=function(t,e){const i=new Map(e.map(t=>[Ai(t.name),t])),s=[],o=[];for(const e of t){const t=i.get(Ai(e.name));t&&(s.push([[e.cx,e.cy],[t.cx,t.cy]]),o.push(e.name))}if(s.length<3)return null;const n=Di(s);return n?{matrix:n,matched:o,residual:Ti(n,s)}:null}(i.rooms,o);n?(n.residual>50&&this._showToast(ti(this._t("vac.autocal_res_warn"),{rooms:String(n.matched.length)})),this._vacSaveMatrix(t.id,e,this._vacMapId(t,i),n.matrix),this._showToast(ti(this._t("vac.autocal_done"),{rooms:String(n.matched.length)}))):this._showToast(this._t("vac.autocal_no_match"))}_vacStartWizard(t){const e=this._vacSource(t),i=e?Pi(this.hass?.states[e]?.attributes):null;e&&i?.pos?(this._markerDialog=null,this._vacCal={markerId:t.id,source:e,mapId:this._vacMapId(t,i),pairs:[]}):this._showToast(this._t("vac.cal_need_pos"))}_vacCalClick(t){const e=this._vacCal;if(!e)return;const i=Pi(this.hass?.states[e.source]?.attributes);if(!i?.pos)return void this._showToast(this._t("vac.cal_need_pos"));const s=this._svgPoint(t),o=[...e.pairs,[[i.pos.x,i.pos.y],[s[0],s[1]]]];if(o.length<3)return void(this._vacCal={...e,pairs:o});const n=Di(o);return n?Ti(n,o)>50&&o.length<4?(this._vacCal={...e,pairs:o},void this._showToast(this._t("vac.cal_res_warn"))):(this._vacSaveMatrix(e.markerId,e.source,e.mapId,n),this._vacCal=null,void this._showToast(this._t("vac.cal_done"))):(this._vacCal={...e,pairs:o.slice(0,-1)},void this._showToast(this._t("vac.cal_degenerate")))}_renderVacuums(t,e){if(this._markup||"decor"===this._mode)return G;const i=[],s=[];for(const o of t){if(o.hidden||!this._isVacDev(o))continue;const t=this._vacSource(o);if(!t)continue;const n=Pi(this.hass?.states[t]?.attributes);if(!n)continue;const r=o.marker?.vacuum?.calibration?.[this._vacMapId(o,n)];if(!r||6!==r.length)continue;const a=this._vacRt.get(o.id),l=a?.moving??!1,c=!l&&a?.endedTs&&Date.now()-a.endedTs<6e5;if(!1!==o.marker?.vacuum?.trail&&(l||c)){const t=n.path||a?.trail||[];if(t.length>1){const e=t.map(([t,e])=>{const[i,s]=Ci(r,t,e);return i.toFixed(1)+","+s.toFixed(1)}).join(" ");s.push(j``)}}if(!l||!n.pos)continue;const[h,d]=Ci(r,n.pos.x,n.pos.y),p=(h-e.x)/e.w*100,u=(d-e.y)/e.h*100,_=a&&a.lastTs>0&&Date.now()-a.lastTs>6e4,m=o.marker?.icon||o.icon||"mdi:robot-vacuum";i.push(B`
{t.stopPropagation();const e=this._vacEntity(o);e&&this._openMoreInfo(e)}}> - ${null!=m?B`
`:G} - +
`)}return i.length||s.length?B` ${s.length?j`${s}`:G} ${i}`:G}_renderDevice(t,e,i=!0,s=!1){const o=this._pos(t),n=(o.x-e.x)/e.w*100,r=(o.y-e.y)/e.h*100;let a=t.hidden?"":this._stateClass(t);s&&"on"===a&&Fi(this.hass,t)&&(a="");const l=t.hidden?null:this._liveTemp(t),c=t.hidden?null:this._liveHum(t),h=!i||t.virtual||t.hidden?null:Ui(this.hass,t.entities),d=t.marker,p=d?.display||"badge",u=("ripple"===p||"icon_ripple"===p)&&!t.hidden,_=t.primary?this.hass.states[t.primary]:void 0,m="value"!==p||t.hidden?null:null!=l?l+"°":null!=c?c+"%":_&&!isNaN(parseFloat(_.state))?parseFloat(_.state)+(_.attributes?.unit_of_measurement?" "+_.attributes.unit_of_measurement:""):null,g=t.primary?t.primary.split(".")[0]:null,f=this._config?.live_states&&!t.hidden?ci(t.icon,g,_?.attributes?.device_class,_?.state,!!d?.icon):t.icon,v=(d?.controls||[]).filter(_i),b=this._config?.live_states&&!t.hidden?v.length?v.map(t=>hi(this.hass.states[t])).find(t=>t)||null:"light"===g?hi(_):null:null,y=this._config?.live_states&&!t.hidden&&function(t,e,i){return"on"===i&&("siren"===t||"binary_sensor"===t&&!!e&&Si.has(e))}(g,_?.attributes?.device_class,_?.state),w=u&&!t.hidden&&!!t.primary&&$e(this.hass.states[t.primary]?.state),x=Number(d?.size)>0?Number(d.size):1,k=Number(d?.angle)||0,$=Number(d?.ripple_size)>0?Number(d.ripple_size):3,S=[`left:${n}%`,`top:${r}%`];return 1!==x&&S.push(`--dev-scale:${x}`),u&&(S.push(`--ripple-scale:${$}`),d?.ripple_color?S.push(`--ripple-color:${d.ripple_color}`):b&&S.push(`--ripple-color:${b}`)),B`