feat: add no-op marker tap action

Issue: #381
User-Visible: yes
This commit is contained in:
Sergey Matyunin
2026-08-30 07:48:16 +00:00
committed by claude[bot]
parent 20cd3241c0
commit ea7803230a
39 changed files with 286 additions and 168 deletions
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5",
"fingerprint": "a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-C_ThwFPM.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 277937,
"initialViewGzipBytes": 277959,
"lazyFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/fr-BTpf9ehd.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/fr-C5zvOMCw.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
],
"lazyGzipBytes": 192258,
"lazyGzipBytes": 192272,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js"
],
"lazyEditorGzipBytes": 140219,
"lazyEditorGzipBytes": 140212,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
],
"lazyOnboardingGzipBytes": 9449,
"lazyOnboardingGzipBytes": 9446,
"lazyLocaleFiles": [
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/fr-BTpf9ehd.js"
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/fr-C5zvOMCw.js"
],
"lazyLocaleGzipBytes": 45544,
"lazyLocaleGzipBytes": 45566,
"files": [
{
"path": "houseplan-assets/backdrop-pick-6gsq-iY1.js",
"sha256": "af7a1ca7b52cd4acda43b4d550cb068d7f6e354855a8976974f70b1d7d85e66d",
"path": "houseplan-assets/backdrop-pick-DCOijtkI.js",
"sha256": "a7fad998c5d0e9b0ce955804a853a88807c36151b66d6ed75b2314828fb15289",
"rawBytes": 6967,
"gzipBytes": 2954,
"gzipBytes": 2952,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BIM0GgdJ.js",
"sha256": "dab65691523e1136dc5ecd3d90716ebd7b3c060d2db4531d4eca6226440e7418",
"rawBytes": 75572,
"gzipBytes": 22993,
"path": "houseplan-assets/de-DIkXscRv.js",
"sha256": "dae9bab2a71cc27a070a51fbf3e086250947d7faa7f709e66954de52a8d7870a",
"rawBytes": 75596,
"gzipBytes": 23002,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-D18CND1l.js",
"sha256": "666445fd34c53ddd9a8e0079149397b27e5433d649046752dd0b288bcdbec11f",
"path": "houseplan-assets/editor-k3fKKmiR.js",
"sha256": "eab211a9b0e3fafbf335d4a0d23567627187e22b61be415c39aebdf843d065a5",
"rawBytes": 3831,
"gzipBytes": 1582,
"gzipBytes": 1581,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-BTpf9ehd.js",
"sha256": "844ff39c35a6afdb5158b975d660ca350257860350ee617fd766406489b57be3",
"rawBytes": 77710,
"gzipBytes": 22551,
"path": "houseplan-assets/fr-C5zvOMCw.js",
"sha256": "fb2f5e7d5dd8feb56402e48d654f3b9a01d204990037432d7da70e36ff59c934",
"rawBytes": 77737,
"gzipBytes": 22564,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-C_ThwFPM.js",
"sha256": "9ed9e68f64d7b15589ab4d869c4aa5d9dbe3f1fafcf0ae2dbeae2cd32a0dfa38",
"rawBytes": 979423,
"gzipBytes": 277142,
"path": "houseplan-assets/houseplan-card-CJnEuDT8.js",
"sha256": "398d050fb4e05b51aab7b82b64477877351f8821c22c08afb5465445be7393cd",
"rawBytes": 979537,
"gzipBytes": 277166,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/fr-BTpf9ehd.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/fr-C5zvOMCw.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"sha256": "ea588ce8cd3a2d4a9d75c36097a80ef56a4f6d195b002ba5a30214c1574b38ef",
"path": "houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"sha256": "54be501a6b3573276dd43567cb429d1a6e3c6b5662b0166de68dd71ef5a79d47",
"rawBytes": 526479,
"gzipBytes": 135683,
"gzipBytes": 135679,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js",
"sha256": "225fb52367703f6767e2c891ac46962d3e986f712bb7a44b6961bbd67c337552",
"path": "houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js",
"sha256": "966191b4dab862615c39b346268780677db1486058b99cccc0950902770ec7d5",
"rawBytes": 26195,
"gzipBytes": 6495,
"gzipBytes": 6494,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "4ea6a5cc96ba771f672613ccc4549512eb4ede8f297d6c60953b307af1b40302",
"sha256": "00832f6b24702d2bb592f0a50e1f9867af0f5de6af1e0d16affbfcd210a08bf8",
"rawBytes": 1183,
"gzipBytes": 795,
"gzipBytes": 793,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-C_ThwFPM.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-CJnEuDT8.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
.computeLabel=${e=>f[e.name]||e.name}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(h.slice(0,3))}
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(h.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-C_ThwFPM.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-6gsq-iY1.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-CJnEuDT8.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-DCOijtkI.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
${s.map(s=>{return _`
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";try{await import("./houseplan-assets/houseplan-card-C_ThwFPM.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";try{await import("./houseplan-assets/houseplan-card-CJnEuDT8.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
+1 -1
View File
@@ -1690,7 +1690,7 @@ MARKER_SCHEMA = vol.Schema(
vol.Optional("model"): _TEXT_OR_NONE,
vol.Optional("link"): vol.Any(None, _URL),
vol.Optional("description"): vol.Any(None, vol.All(str, vol.Length(max=MAX_DESCRIPTION))),
vol.Optional("tap_action"): vol.Any("info", "more-info", "toggle", "run", "cover", None),
vol.Optional("tap_action"): vol.Any("info", "more-info", "toggle", "run", "none", "cover", None),
# the 'run' target: only the runnable domains, nothing else is callable
vol.Optional("tap_target"): vol.Any(
None, vol.All(str, vol.Length(max=MAX_TEXT), vol.Match(r"^(automation|script|scene)\.[A-Za-z0-9_]+$"))
+47 -3
View File
@@ -4,7 +4,7 @@ const res = await page.evaluate(async () => {
const out = {};
const c = window.__card;
const sr = () => c.shadowRoot || c.renderRoot;
// 1) Единый селектор содержит ровно четыре актуальных действия;
// 1) Единый селектор содержит ровно пять актуальных действий;
// legacy cover и пустая авто-опция в UI не возвращаются.
c._setMode('devices'); await c.updateComplete;
// v1.39.0: у ЛАМП дефолт 'toggle', поэтому для проверки дефолта 'info'
@@ -13,8 +13,8 @@ const res = await page.evaluate(async () => {
c._openMarkerDialog(dev); await c.updateComplete;
const sel = sr().querySelector('#marker-tap-action');
const values = sel ? [...sel.options].map((o) => o.value) : [];
out.fourCurrentOptions = JSON.stringify(values) === JSON.stringify([
'info', 'more-info', 'toggle', 'run',
out.fiveCurrentOptions = JSON.stringify(values) === JSON.stringify([
'info', 'more-info', 'toggle', 'run', 'none',
]);
out.noLegacyOptions = !values.includes('') && !values.includes('cover');
out.defaultInfo = sel && sel.value === 'info';
@@ -55,6 +55,50 @@ const res = await page.evaluate(async () => {
out.cardTapIgnored = calls.length === 0 && !!c._infoCard;
c._infoCard = null;
} else out.cardTapIgnored = 'no-plain-device';
// 6) Явный none поглощает click/keyboard, но не запускает ни один эффект.
if (dev) {
const originalDevices = c._devices;
const noOp = { ...dev, tapAction: 'none', marker: { ...dev.marker, tap_action: 'none' } };
c._devices = originalDevices.map((item) => item.id === dev.id ? noOp : item);
let bindingChecks = 0;
let feedback = 0;
let toasts = 0;
let wsCalls = 0;
let serviceCalls = 0;
const originalBindingActive = c._deviceBindingActive;
const originalFeedback = c._startDevicePressFeedback;
const originalToast = c._showToast;
const originalHass = c.hass;
const originalCallWS = c.hass.callWS;
const originalCallService = c.hass.callService;
c._deviceBindingActive = () => { bindingChecks += 1; return true; };
c._startDevicePressFeedback = () => { feedback += 1; };
c._showToast = () => { toasts += 1; };
c.hass = {
...c.hass,
callWS: (...args) => { wsCalls += 1; return originalCallWS(...args); },
callService: (...args) => { serviceCalls += 1; return originalCallService(...args); },
};
c._infoCard = null;
c._tapConfirm = null;
let stopped = false;
c._clickDevice({ stopPropagation() { stopped = true; } }, noOp);
const enter = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true });
c._keyDevice(enter, noOp);
const space = new KeyboardEvent('keydown', { key: ' ', cancelable: true });
c._keyDevice(space, noOp);
out.noneConsumesActivation = stopped && enter.defaultPrevented && space.defaultPrevented;
out.noneHasNoEffects = bindingChecks === 0 && feedback === 0 && toasts === 0
&& wsCalls === 0 && serviceCalls === 0 && c._infoCard === null && c._tapConfirm === null;
c._devices = originalDevices;
c._deviceBindingActive = originalBindingActive;
c._startDevicePressFeedback = originalFeedback;
c._showToast = originalToast;
c.hass = originalHass;
} else {
out.noneConsumesActivation = false;
out.noneHasNoEffects = false;
}
return out;
});
// значения зафиксированы прогоном на v1.43.1 и сверены с кодом (audit T1)
+58 -58
View File
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5",
"fingerprint": "a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-C_ThwFPM.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 277937,
"initialViewGzipBytes": 277959,
"lazyFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/fr-BTpf9ehd.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/fr-C5zvOMCw.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
],
"lazyGzipBytes": 192258,
"lazyGzipBytes": 192272,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js"
],
"lazyEditorGzipBytes": 140219,
"lazyEditorGzipBytes": 140212,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
],
"lazyOnboardingGzipBytes": 9449,
"lazyOnboardingGzipBytes": 9446,
"lazyLocaleFiles": [
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/fr-BTpf9ehd.js"
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/fr-C5zvOMCw.js"
],
"lazyLocaleGzipBytes": 45544,
"lazyLocaleGzipBytes": 45566,
"files": [
{
"path": "houseplan-assets/backdrop-pick-6gsq-iY1.js",
"sha256": "af7a1ca7b52cd4acda43b4d550cb068d7f6e354855a8976974f70b1d7d85e66d",
"path": "houseplan-assets/backdrop-pick-DCOijtkI.js",
"sha256": "a7fad998c5d0e9b0ce955804a853a88807c36151b66d6ed75b2314828fb15289",
"rawBytes": 6967,
"gzipBytes": 2954,
"gzipBytes": 2952,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BIM0GgdJ.js",
"sha256": "dab65691523e1136dc5ecd3d90716ebd7b3c060d2db4531d4eca6226440e7418",
"rawBytes": 75572,
"gzipBytes": 22993,
"path": "houseplan-assets/de-DIkXscRv.js",
"sha256": "dae9bab2a71cc27a070a51fbf3e086250947d7faa7f709e66954de52a8d7870a",
"rawBytes": 75596,
"gzipBytes": 23002,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-D18CND1l.js",
"sha256": "666445fd34c53ddd9a8e0079149397b27e5433d649046752dd0b288bcdbec11f",
"path": "houseplan-assets/editor-k3fKKmiR.js",
"sha256": "eab211a9b0e3fafbf335d4a0d23567627187e22b61be415c39aebdf843d065a5",
"rawBytes": 3831,
"gzipBytes": 1582,
"gzipBytes": 1581,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-BTpf9ehd.js",
"sha256": "844ff39c35a6afdb5158b975d660ca350257860350ee617fd766406489b57be3",
"rawBytes": 77710,
"gzipBytes": 22551,
"path": "houseplan-assets/fr-C5zvOMCw.js",
"sha256": "fb2f5e7d5dd8feb56402e48d654f3b9a01d204990037432d7da70e36ff59c934",
"rawBytes": 77737,
"gzipBytes": 22564,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-C_ThwFPM.js",
"sha256": "9ed9e68f64d7b15589ab4d869c4aa5d9dbe3f1fafcf0ae2dbeae2cd32a0dfa38",
"rawBytes": 979423,
"gzipBytes": 277142,
"path": "houseplan-assets/houseplan-card-CJnEuDT8.js",
"sha256": "398d050fb4e05b51aab7b82b64477877351f8821c22c08afb5465445be7393cd",
"rawBytes": 979537,
"gzipBytes": 277166,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BIM0GgdJ.js",
"houseplan-assets/editor-D18CND1l.js",
"houseplan-assets/fr-BTpf9ehd.js",
"houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js"
"houseplan-assets/de-DIkXscRv.js",
"houseplan-assets/editor-k3fKKmiR.js",
"houseplan-assets/fr-C5zvOMCw.js",
"houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-BGFK1d9o.js",
"sha256": "ea588ce8cd3a2d4a9d75c36097a80ef56a4f6d195b002ba5a30214c1574b38ef",
"path": "houseplan-assets/houseplan-editor-runtime-WlKSd42q.js",
"sha256": "54be501a6b3573276dd43567cb429d1a6e3c6b5662b0166de68dd71ef5a79d47",
"rawBytes": 526479,
"gzipBytes": 135683,
"gzipBytes": 135679,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-Ji0RleOQ.js",
"sha256": "225fb52367703f6767e2c891ac46962d3e986f712bb7a44b6961bbd67c337552",
"path": "houseplan-assets/houseplan-onboarding-runtime-DUciEEG9.js",
"sha256": "966191b4dab862615c39b346268780677db1486058b99cccc0950902770ec7d5",
"rawBytes": 26195,
"gzipBytes": 6495,
"gzipBytes": 6494,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-6gsq-iY1.js",
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/backdrop-pick-DCOijtkI.js",
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "4ea6a5cc96ba771f672613ccc4549512eb4ede8f297d6c60953b307af1b40302",
"sha256": "00832f6b24702d2bb592f0a50e1f9867af0f5de6af1e0d16affbfcd210a08bf8",
"rawBytes": 1183,
"gzipBytes": 795,
"gzipBytes": 793,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-C_ThwFPM.js"
"houseplan-assets/houseplan-card-CJnEuDT8.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,14 +1,14 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-C_ThwFPM.js";class f extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,f={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},h=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";import{c as e,l as o,t,c$ as s,A as a,d0 as i,d1 as l,d2 as n,E as r,b as c}from"./houseplan-card-CJnEuDT8.js";class h extends e{constructor(){super(...arguments),this._spaces=null,this._spacesLoading=!1,this._spacesAuthoritative=!1}setConfig(e){this._config=e}async _loadSpaces(){if(!this._spaces&&!this._spacesLoading&&this.hass){this._spacesLoading=!0;try{const e=await this.hass.callWS({type:"houseplan/config/get"});this._spaces=(e?.config?.spaces||[]).map(e=>({value:e.id,label:e.title||e.id})),this._spacesAuthoritative=!0}catch{this._spaces=[],this._spacesAuthoritative=!1}finally{this._spacesLoading=!1}}}get _lang(){return o(this.hass,this._config?.language)}get _floorToken(){const e=this._config?.floor;return"number"==typeof e?`__houseplan_yaml_floor_index__:${String(e)}`:null}get _formData(){const e={...this._config},o=this._floorToken;return o?e.floor=o:Object.prototype.hasOwnProperty.call(e,"floor")||(e.floor=""),e}get _schema(){const e=this._spaces||[],o=this._lang,a=[{value:"",label:t(o,"editor.floor_none")}],i=this._floorToken;i&&a.push({value:i,label:t(o,"editor.floor_index",{index:String(this._config?.floor)})});const l="string"==typeof this._config?.floor?this._config.floor:"";l&&!e.some(e=>e.value===l)&&a.push({value:l,label:l}),a.push(...e);const n="string"==typeof this._config?.default_floor?this._config.default_floor:"",r=[...e];return n&&!e.some(e=>e.value===n)&&r.unshift({value:n,label:n}),[{name:"title",selector:{text:{}}},{name:"floor",selector:{select:{mode:"dropdown",options:a}}},e.length?{name:"default_floor",selector:{select:{mode:"dropdown",options:r}}}:{name:"default_floor",selector:{text:{}}},{name:"language",selector:{select:{mode:"dropdown",options:s(t(o,"editor.lang_auto"),this._config?.language)}}},{name:"icon_size",selector:{number:{min:1,max:6,step:.1,mode:"box"}}},{name:"show_temperature",selector:{boolean:{}}},{name:"live_states",selector:{boolean:{}}},{name:"show_signal",selector:{boolean:{}}},{name:"kiosk",selector:{boolean:{}}},{name:"cycle",selector:{number:{min:0,max:3600,step:5,mode:"box"}}}]}render(){if(!this.hass||!this._config)return a;const e=i(this,l,o(this.hass,this._config.language));if("cold"===e)return n();if("warm"===e)return r;this._loadSpaces();const s=this._lang,h={title:t(s,"editor.title"),floor:t(s,"editor.floor"),default_floor:t(s,"editor.default_floor"),language:t(s,"editor.language"),icon_size:t(s,"editor.icon_size"),show_temperature:t(s,"editor.show_temperature"),live_states:t(s,"editor.live_states"),show_signal:t(s,"editor.show_signal"),kiosk:t(s,"editor.kiosk"),cycle:t(s,"editor.cycle")},f=this._schema,_=function(e,o,t){if(!t||null===o)return null;const s="string"==typeof e?.default_floor?e.default_floor:"";return!s||o.some(e=>e.value===s)?null:s}(this._config,this._spaces,this._spacesAuthoritative),d=e=>c`<ha-form
.hass=${this.hass}
.data=${this._formData}
.schema=${e}
.computeLabel=${e=>f[e.name]||e.name}
.computeLabel=${e=>h[e.name]||e.name}
@value-changed=${this._valueChanged}
></ha-form>`;return c`
${d(h.slice(0,3))}
${d(f.slice(0,3))}
${_?c`<div class="default-floor-error" role="alert"
style="color:var(--error-color,#db4437);margin:-4px 0 12px;overflow-wrap:anywhere">
${t(s,"editor.default_floor_missing",{id:_})}
</div>`:a}
${d(h.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}f.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",f);
${d(f.slice(3))}
`}_valueChanged(e){const o={...this._config,...e.detail.value};""===o.floor?delete o.floor:o.floor===this._floorToken&&(o.floor=this._config?.floor);const t=new Event("config-changed",{bubbles:!0,composed:!0});t.detail={config:o},this.dispatchEvent(t)}}h.properties={hass:{attribute:!1},_config:{state:!0},_spaces:{state:!0}},customElements.get("houseplan-card-editor")||customElements.define("houseplan-card-editor",h);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-C_ThwFPM.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-6gsq-iY1.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";import{bT as t,bU as s,bV as e,bW as o,aq as a,bX as l,c0 as i,bY as h,bZ as c,b_ as n,b$ as r,A as p,b as _,cT as d,ap as u,ar as g,cU as m}from"./houseplan-card-CJnEuDT8.js";import{i as b,c as $,e as v,r as f,a as y,b as w,s as D,t as S}from"./backdrop-pick-DCOijtkI.js";const k=1e3,C=t=>{if(!t.trim())return null;const s=Number(t.replace(",","."));return Number.isFinite(s)?s:null},M="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";class F{constructor(t){this.host=t,this._toggleServerPlans=async()=>{const t=this.host._spaceDialog;if(t)if(t.pickSaved)this.host._spaceDialog={...t,pickSaved:!1};else{this.host._spaceDialog={...t,pickSaved:!0,savedBusy:!0};try{const t=await this.host.hass.callWS({type:"houseplan/plans/list"}),s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:t?.plans||[],savedBusy:!1})}catch(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,saved:[],savedBusy:!1}),this.host._showToast(this.host._t("toast.plans_list_failed",{err:this.host._errText(t)}))}}}}_openSpaceDialog(p,_){if(!this.host._serverStorage||!this.host._serverCfg)return void this.host._showToast(this.host._t("toast.integration_missing"));if("edit"===p){const i=this.host._serverCfg.spaces.find(t=>t.id===_);if(!i)return;const h=t(i),c=i.settings?.custom_fill&&"object"==typeof i.settings.custom_fill?s(i.settings.custom_fill):null,n="none"===h.fill?{...c||e,a:0}:c;return void(this.host._spaceDialog={mode:p,spaceId:_,title:i.title,planUrl:i.plan_url||null,planFile:null,source:i.plan_url?"file":"draw",showBorders:h.showBorders,showNames:h.showNames,zeroWallStyle:l(i),displayTouched:!0,hideDecor:h.hideDecor,hideOpenings:h.hideOpenings,roomColor:h.color,roomOpacity:h.opacity,fillMode:"none"===h.fill?"custom":h.fill,customFill:n,glowEnabled:h.glow,bgColor:h.bgColor,bgMode:"static"===i.settings?.bg_mode||"daynight"===i.settings?.bg_mode?i.settings.bg_mode:null,northDeg:a({},i.settings),sunRays:"boolean"==typeof i.settings?.sun_rays?i.settings.sun_rays:null,tempMin:h.tempMin,tempMax:h.tempMax,showLqi:h.showLqi??this.host._config?.show_signal??!0,cardFontScale:h.cardFontScale,labelTemp:h.labelTemp,labelHum:h.labelHum,labelLqi:h.labelLqi,labelLight:h.labelLight,cellCm:Number(i.cell_cm)>0?Number(i.cell_cm):5,cellCmInput:o(Number(i.cell_cm)>0?Number(i.cell_cm):5,this.host._imperial),cellCmTouched:!1,busy:!1})}const d=i(this.host._imperial);this.host._spaceDialog={mode:p,title:"",planUrl:null,planFile:null,...b(),hideDecor:!1,hideOpenings:!1,zeroWallStyle:"dashed",roomColor:r,roomOpacity:n,fillMode:"custom",customFill:{...e,a:0},glowEnabled:!0,bgColor:null,bgMode:"daynight",northDeg:null,sunRays:null,tempMin:c,tempMax:h,showLqi:this.host._config?.show_signal??!0,cardFontScale:1,labelTemp:!1,labelHum:!1,labelLqi:!1,labelLight:!1,cellCm:d,cellCmInput:o(d,this.host._imperial),cellCmTouched:!1,busy:!1}}async _pickPlanFile(t){const s=t.target,e=s.files?.[0];if(!e||!this.host._spaceDialog)return;s.value="";const o=await $(e);if("reject"===o.kind)return void this.host._showToast(this.host._t("toast.plan_formats"));if("guard"===o.kind)return void(this.host._backdropGuard=o.state);const a=await v(e,o.ext,e.name);this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:a})}_renderBackdropGuard(){return f(this.host,t=>{this.host._spaceDialog&&(this.host._spaceDialog={...this.host._spaceDialog,planFile:t})},()=>{this.host._backdropGuard=null},this.host.hass)??p}_useServerPlan(t){const s=this.host._spaceDialog;s&&(this.host._spaceDialog={...s,planUrl:t,planFile:null,pickSaved:!1,savedAspect:void 0},this.host._aspectJob=this._readPlanAspect(t))}async _readPlanAspect(t){for(let s=0;s<40;s++){const s=this.host._display(t);if(s){const e=await new Promise(t=>{const e=new Image;e.onload=()=>t(e.naturalWidth&&e.naturalHeight?e.naturalWidth/e.naturalHeight:0),e.onerror=()=>t(0),e.src=s}),o=this.host._spaceDialog;return o&&o.planUrl===t&&Number.isFinite(e)&&e>0?(this.host._spaceDialog={...o,savedAspect:e},e):0}if(await new Promise(t=>setTimeout(t,150)),this.host._spaceDialog?.planUrl!==t)return 0}return 0}async _deleteServerPlan(t){if(confirm(this.host._t("confirm.delete_plan",{name:t})))try{await this.host.hass.callWS({type:"houseplan/plans/delete",name:t});const s=this.host._spaceDialog;s?.saved&&(this.host._spaceDialog={...s,saved:s.saved.filter(s=>s.name!==t)})}catch(t){this.host._showToast(this.host._t("toast.plan_delete_failed",{err:this.host._errText(t)}))}}_renderServerPlans(t){if(t.savedBusy)return _`<div class="savedplans muted">${this.host._t("space.loading")}</div>`;const s=t.saved||[];if(!s.length)return _`<div class="savedplans muted">${this.host._t("space.no_saved")}</div>`;return _`<div class="savedplans">
${s.map(s=>{return _`
<div class="savedplan ${s.url===t.planUrl?"cur":""}">
<img src=${this.host._display(s.url)} alt="" loading="lazy" decoding="async" />
+1 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d301cf55cc41f758a4c82b05cc37f4a616cabbfeff496053afc2dc62fd2374e5";try{await import("./houseplan-assets/houseplan-card-C_ThwFPM.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="a42766a5eece6ca11b0a2f60be712efe4cfe5958aac5e8d60007f177f40a6052";try{await import("./houseplan-assets/houseplan-card-CJnEuDT8.js")}catch(e){if(!customElements.get("houseplan-card")){const l=String(navigator.language||"en").toLowerCase();const m=l.startsWith("ru")?"House Plan обновился — перезагрузите страницу (Ctrl+F5).":l.startsWith("de")?"House Plan wurde aktualisiert — bitte laden Sie die Seite neu (Strg+F5).":l.startsWith("fr")?"House Plan a été mis à jour — veuillez recharger la page (Ctrl+F5).":"House Plan was updated — please reload the page (Ctrl+F5).";customElements.define("houseplan-card",class extends HTMLElement{setConfig(){}getCardSize(){return 1}connectedCallback(){this.style.cssText="display:block;box-sizing:border-box;padding:16px;border:1px solid var(--divider-color,#e0e0e0);border-radius:var(--ha-card-border-radius,12px);background:var(--card-background-color,#fff);color:var(--primary-text-color,#212121);font:14px/1.4 var(--paper-font-body1_-_font-family,sans-serif)";this.textContent=m}})}console.error("[houseplan] stale entry: the main chunk is unavailable",e)}
+6
View File
@@ -1389,6 +1389,12 @@ its configured space.
action `cover` remains accepted and losslessly round-tripped as a legacy
origin until the user deliberately changes the selector. An absent action on
a primary `light.*` likewise stays absent on an untouched Open → Save.
The canonical explicit `tap_action=none` (#381) is resolved separately from
that absent default. `_clickDevice()` first consumes propagation and
re-resolves the current marker by stable id, then returns on `none` before
capability lookup, confirmation, cards/toasts, press feedback or HA
dispatch. Keyboard Enter/Space shares the same path; hold and context-menu
handlers remain independent.
`POWER_ADAPTERS` is the explicit domain allow-list and carries per-entity HA
feature masks where a domain-wide service is not capability proof. The
service catalog is a second fail-closed guard. A click resolves the current
+5
View File
@@ -2,6 +2,11 @@
## Unreleased
- Device markers can now use **Do nothing** as their tap action. Short click,
tap, Enter and Space are then quiet no-ops, while marker appearance,
long-press House Plan details and right-click HA more-info remain available
([#381](https://github.com/Matysh/houseplan-card/issues/381)).
## v1.69.0 — 2026-08-30
- House Plan now speaks German and French, keeps the editors outside the
+6
View File
@@ -8,6 +8,12 @@
## Не выпущено
- Для маркера устройства теперь можно выбрать действие **«Ничего не делать»**.
Короткий клик, тап, Enter и Space ничего не открывают и не отправляют, при
этом внешний вид маркера, карточка House Plan по долгому нажатию и HA
more-info по правому клику сохраняются
([#381](https://github.com/Matysh/houseplan-card/issues/381)).
## v1.69.0 — 2026-08-30
- House Plan заговорил по-немецки и по-французски, редакторы больше не входят в
+12
View File
@@ -283,6 +283,18 @@ migration: the new field exists only in the portable envelope.
## Legacy device tap action
The canonical marker token `tap_action: none` is an explicit saved no-op. It is
different from an absent, `null` or empty action: those values retain the
domain default (Toggle for a primary `light.*`, Device card otherwise). The
current frontend consumes short click/tap and keyboard activation before any
capability, UI or HA dispatch, while hold and context-menu paths are unchanged;
the current backend accepts the literal. Generic full/space transfer preserves
it without migration, while virtual duplication continues to omit tap action
with the other device-specific behaviour. On downgrade, an older frontend
safely projects the unknown token to Device card, but an older backend rejects
a subsequent config write containing it. Therefore rollback must keep backend
read/write acceptance until stored `none` values have been migrated to `info`.
The historical marker token `tap_action: cover` remains accepted indefinitely.
It is projected in the current UI as the universal **Toggle state** action and
keeps cover-first target priority at runtime. Merely opening and saving an
+6 -3
View File
@@ -1548,15 +1548,18 @@ separately promised workflows:
`can_write` warm restore, one press on the visible editor close button
cancels the deferred editor and a late response cannot reopen it (#95)
[auto: smoke_nav_persist]
- [ ] Tap action cleanup + right click (v1.38.1, #94): the per-device action
list has four options (Device card / HA more-info / Toggle state / Run),
- [ ] Tap action cleanup + right click (v1.38.1, #94; #381): the per-device
action list has five options (Device card / HA more-info / Toggle state /
Run / Do nothing),
no separate cover or "card default" option — the card editor's global
tap option is gone and ignored;
Device card opens locally for compound curtains/covers even during a
transient registry revalidation and calls no HA service (#96)
[auto: smoke_cover_not_primary];
right click on an icon in VIEW opens HA more-info (native menu kept in
editors; virtual w/o entity → device card) [auto: smoke_tap_ctx]
editors; virtual w/o entity → device card); explicit Do nothing consumes
short click, Enter and Space without card, confirmation, feedback, toast
or HA call [auto: smoke_tap_ctx]
- [ ] Binding section redesign (v1.38.0): two radios — Virtual / Pick from
the HA list — with a "Show entities" checkbox (tooltip) next to the
second; the dropdown (search inside) appears only in HA mode, opens
+4 -1
View File
@@ -569,6 +569,7 @@ aggregates until the same ID becomes active again.
| HA more-info | Native dialog for the exact primary entity | No state change |
| Toggle state | Toggles the exact binding, supported device function or configured light-source group | Locks, alarm panels and protective garage/door/gate targets are no-op; confirmation is optional |
| Run | Runs an automation, script or scene | Explicit target; confirmation is optional |
| Do nothing | Short click/tap, Enter and Space are ignored | No dialog, command, confirmation, toast or press feedback |
When confirmation is enabled for **Toggle state**, the dialog shows the current
state and the exact expected result (`On`, `Off`, `Open`, `Closed` or `Stopped`).
@@ -580,7 +581,9 @@ asks you to try again.
A light defaults to Toggle; other devices default to the House Plan card. An
unsupported Toggle remains a visible no-op and is never changed into another
action behind the user's back.
action behind the user's back. **Do nothing** is an explicit saved choice, not
the default: it keeps the marker's normal appearance, hover, long-press House
Plan card and right-click HA more-info while disabling only short activation.
If every explicitly configured `controls` target is unavailable, missing or
disabled in HA, a short tap sends no service call and the standard local House
+8 -2
View File
@@ -809,7 +809,7 @@ binding tombstone не вырезает её из живого устройст
| Имя | Подпись и заголовок карточки устройства |
| Привязка | Устройство, сущность или виртуальный маркер |
| Комната | Автоматическая HA-зона либо ручное назначение, включая комнату без зоны |
| Действие по нажатию | Карточка House Plan, HA more-info, универсальное **Переключить состояние** или запуск сценария. Под селектором сразу показаны точная цель, текущее состояние и результат следующего нажатия либо причина, почему нажатие ничего не сделает |
| Действие по нажатию | Карточка House Plan, HA more-info, универсальное **Переключить состояние**, запуск сценария или явное **Ничего не делать**. Под селектором сразу показаны точная цель, текущее состояние и результат следующего нажатия либо причина, почему нажатие ничего не сделает |
| Управляет другими источниками света | Группа других `light.*`/`switch.*` и принудительных источников, уже размещённых на плане. Собственная entity и сущности привязанного устройства сюда не входят |
| Является источником света | **Авто** использует роль устройства, **Всегда** создаёт собственный источник даже без entity HA, **Никогда** исключает только собственный источник. Внешние `controls` продолжают учитываться в свете комнаты |
| Ведущая сущность | Появляется для роли **Всегда**, если у устройства несколько `light.*`/`switch.*`. Выбранная сущность определяет состояние и service call; исчезнувший выбор не стирается и временно заменяется безопасным fallback |
@@ -926,6 +926,7 @@ HA. «Показать» заблокировано до активации. П
| HA more-info | Открывается штатный диалог основной сущности | Состояние не меняется |
| Переключить состояние | Переключает точную привязанную сущность, функциональную сущность устройства либо явно настроенную группу `controls`. Для штор/жалюзи и клапанов автоматически выбирает open/close/stop | Замки, охранные панели и защитные `garage`/`door`/`gate` остаются безопасным no-op; можно включить подтверждение |
| Run | `automation.trigger`, `script.turn_on` или `scene.turn_on` | Цель выбирается явно; можно включить подтверждение |
| Ничего не делать | Короткий клик/тап, Enter и Space игнорируются | Нет карточки, команды, подтверждения, уведомления и анимации нажатия |
Если для **«Переключить состояние»** включено подтверждение, диалог показывает
текущее состояние и точный ожидаемый результат: «Включено», «Выключено»,
@@ -935,7 +936,12 @@ HA. «Показать» заблокировано до активации. П
House Plan заново определяет состояние и направление. Если набор целей успел
измениться, действие отменяется с предложением повторить попытку.
У устройства, чья основная функция — `light.*`, действием по умолчанию уже является **Переключить состояние**. У остальных устройств по умолчанию открывается внутренняя карточка.
У устройства, чья основная функция — `light.*`, действием по умолчанию уже
является **Переключить состояние**. У остальных устройств по умолчанию
открывается внутренняя карточка. **Ничего не делать** — отдельный сохранённый
выбор, а не новое значение по умолчанию: внешний вид, ховер, долгая House Plan-
карточка и HA more-info по правому клику сохраняются, отключается только
короткая активация.
Если у маркера с привязкой к устройству есть две или больше собственных
сущностей `light.*`/`switch.*`, под действием появляется **«Переключаемая
+1 -1
View File
@@ -1,7 +1,7 @@
# Issue #381 — действие по нажатию «Ничего не делать»
- **Issue:** https://github.com/Matysh/houseplan-card/issues/381
- **Статус документа:** вторая редакция, замечание r1 устранено, готова к ревью
- **Статус документа:** утверждено, реализовано, готово к code review
- **Приоритет / тип:** P2 · feature / polish
- **Область:** marker config, Редактор устройств, View/киоск, frontend action
projection, backend validation, i18n, документация и QA
+4 -3
View File
@@ -15,7 +15,7 @@ import {
type ResolvedLightSource,
type IncomingLightControl,
} from './devices';
import { COVER_GUARDED_CLASSES, isControllable } from './logic';
import { COVER_GUARDED_CLASSES, isControllable, type DeviceTapAction } from './logic';
import type { DevItem } from './types';
import {
isManualVirtualLightMarker,
@@ -463,9 +463,10 @@ function emptyIntent(origin: ToggleOrigin, reason: ToggleNoneReason): ResolvedTo
export function projectedTapAction(
persisted: string | null | undefined,
defaultDomain: string | null | undefined,
): 'info' | 'more-info' | 'toggle' | 'run' {
): DeviceTapAction {
if (persisted === 'cover' || persisted === 'toggle') return 'toggle';
if (persisted === 'more-info' || persisted === 'run' || persisted === 'info') return persisted;
if (persisted === 'more-info' || persisted === 'run'
|| persisted === 'info' || persisted === 'none') return persisted;
// Only actual absence activates the light default. An unknown persisted
// token is invalid data, not an absent choice: fail closed to the local card
// so the UI projection and `toggleOriginOf()` cannot disagree.
+3
View File
@@ -5204,6 +5204,9 @@ export class HouseplanCard extends LitElement {
const action = projectedTapAction(
actionDevice.tapAction, actionDevice.primary?.split('.')[0],
);
// An explicit no-op still owns the click: propagation was stopped and the
// current marker was resolved, but no capability, feedback or UI path runs.
if (action === 'none') return;
// the accidental-tap guard (owner's spec 2026-07-29): any state-changing
// action — toggle or run — may ask first. The dialog is ours, not the
// browser confirm(), so it works and looks right on a wall tablet.
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Das vollständige Home Assistant-Register ist für diesen Benutzer nicht verfügbar. Das nicht verifizierte Objekt kann vorerst nicht angezeigt oder verwendet werden.",
"marker.delete_tip": "Löschen Sie das Gerät vollständig aus dem Plan und allen Aggregaten. Sie können es später wieder hinzufügen.",
"tap.run": "Führen Sie Automation/Skript/Szene aus",
"tap.none": "Nichts tun",
"marker.run_target_label": "Was ausführen",
"marker.run_search_ph": "Suchen: Automation, Skript oder Szene…",
"marker.run_target_gone": "Ziel {id} nicht gefunden — erneut auswählen",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "The full Home Assistant registry is unavailable to this user. The unverified object cannot be shown or used for now.",
"marker.delete_tip": "Completely delete the device from the plan and every aggregate. You can add it again later.",
"tap.run": "Run automation/script/scene",
"tap.none": "Do nothing",
"marker.run_target_label": "What to run",
"marker.run_search_ph": "Search: automation, script or scene…",
"marker.run_target_gone": "Target {id} not found — pick again",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Le registre Home Assistant complet n’est pas accessible à cet utilisateur. L’objet non vérifié ne peut pas être affiché ni utilisé pour le moment.",
"marker.delete_tip": "Supprimer complètement l’appareil du plan et de tous les agrégats. Vous pourrez l’ajouter de nouveau plus tard.",
"tap.run": "Exécuter une automatisation/un script/une scène",
"tap.none": "Ne rien faire",
"marker.run_target_label": "Élément à exécuter",
"marker.run_search_ph": "Rechercher : automatisation, script ou scène…",
"marker.run_target_gone": "Cible {id} introuvable — choisissez-en une autre",
+1
View File
@@ -729,6 +729,7 @@
"marker.ha_registry_limited": "Полный реестр Home Assistant недоступен этому пользователю. Неподтверждённый объект временно нельзя показывать или использовать.",
"marker.delete_tip": "Полностью удалить устройство с плана и из всех расчётов. Позже его можно добавить заново.",
"tap.run": "Запустить автоматизацию/скрипт/сцену",
"tap.none": "Ничего не делать",
"marker.run_target_label": "Что запускать",
"marker.run_search_ph": "Поиск: автоматизация, скрипт или сцена…",
"marker.run_target_gone": "Цель {id} не найдена — выберите заново",
+2 -1
View File
@@ -907,7 +907,8 @@ export function normalizeDeviceDisplay(value: unknown): DeviceDisplayMode {
? value as DeviceDisplayMode : 'badge';
}
/** Current editor choices. `cover` remains a read/backend compatibility token. */
export const TAP_ACTIONS = ['info', 'more-info', 'toggle', 'run'] as const;
export const TAP_ACTIONS = ['info', 'more-info', 'toggle', 'run', 'none'] as const;
export type DeviceTapAction = typeof TAP_ACTIONS[number];
/** Persisted space-level data fills. `none` remains a read-compatibility token;
* the space editor projects it to `custom` because every room has a visible
* base colour and the user can now choose that colour directly. */
@@ -64,6 +64,23 @@ test('issue 362 makes the whole device package inert only in Background', () =>
assert.match(render, /@pointermove=\$\{[\s\S]*this\._mode !== 'view' && this\._mode !== 'devices'/);
});
test('issue 381 consumes an explicit no-op before any action side effect', () => {
const card = source('houseplan-card.ts');
const click = card.slice(
card.indexOf('private _clickDevice('),
card.indexOf('private _showUnavailableToggleTargets('),
);
const stop = click.indexOf('ev.stopPropagation();');
const live = click.indexOf('const actionDevice = this._devices.find');
const projection = click.indexOf('const action = projectedTapAction(');
const noOp = click.indexOf("if (action === 'none') return;");
const guard = click.indexOf('const guarded =');
const toggle = click.indexOf("if (action === 'toggle')");
assert.ok(stop >= 0 && live > stop && projection > live);
assert.ok(noOp > projection && guard > noOp && toggle > guard);
assert.doesNotMatch(click.slice(projection, noOp), /callService|callWS|_showToast|_infoCard|_startDevicePressFeedback/);
});
test('issue 213 projects opening locks through the compact package layers', () => {
const styles = source('styles.ts');
const card = source('houseplan-card.ts');
+2
View File
@@ -83,6 +83,8 @@ test('action projection is universal while persisted legacy/default origins stay
assert.equal(projectedTapAction('', 'light'), 'toggle');
assert.equal(projectedTapAction(undefined, 'switch'), 'info');
assert.equal(projectedTapAction('future-action', 'light'), 'info');
assert.equal(projectedTapAction('none', 'light'), 'none');
assert.equal(projectedTapAction('none', 'switch'), 'none');
assert.equal(toggleOriginOf(device({ tapAction: 'toggle' })), 'explicit-toggle');
assert.equal(toggleOriginOf(device({ tapAction: 'cover' })), 'legacy-cover');
assert.equal(toggleOriginOf(device({ tapAction: null, primary: 'light.lamp' })), 'default-light');
+5 -1
View File
@@ -21,7 +21,7 @@ import {
openingShoulders, openingEntityReferences, fillColorsOf, lerpColor, roomFillStyle,
resolveEffectiveRoomFill, stateIcon, lightColorOf, isAlarmState, parseRoomRef,
diffNewDevices, poleOfInaccessibility, runServiceFor, coverMoving,
normalizeDeviceDisplay, isAlarmCapable,
normalizeDeviceDisplay, isAlarmCapable, TAP_ACTIONS,
liveText, liveTextValue, liveTextReference, liveTextToken,
hassValue, valueWithUnit, decorTextScale, decorTextLines,
LIVE_TEXT_DASH, LIVE_TEXT_VALUE_MAX, DECOR_TEXT_SCALE_MIN, DECOR_TEXT_SCALE_MAX,
@@ -31,6 +31,10 @@ import {
iconFor, compileIconRules, isValidPattern, iconFromDeviceClasses,
} from '../test-build/rules.js';
test('tap action editor choices keep the canonical order', () => {
assert.deepEqual([...TAP_ACTIONS], ['info', 'more-info', 'toggle', 'run', 'none']);
});
test('display normalization and alarm-capable metadata share one contract', () => {
assert.equal(normalizeDeviceDisplay(undefined), 'badge');
assert.equal(normalizeDeviceDisplay('ripple'), 'icon_ripple');
+1
View File
@@ -887,6 +887,7 @@ def test_run_target_is_bounded_to_runnable_domains():
def test_every_tap_action_the_editor_offers_is_accepted():
assert "none" in _ts_list("TAP_ACTIONS")
for action in _ts_list("TAP_ACTIONS"):
v.MARKER_SCHEMA(_marker(tap_action=action))
with pytest.raises(vol.Invalid):