Compare commits

...
Author SHA1 Message Date
Sergey Matyunin b2470df5a7 Fix stable promotion version-source gate
Allow only mechanically proven version-declaration changes in the three canonical source files while keeping every other release source diff fail-closed.

Issue: #379
User-Visible: no
2026-08-30 00:05:41 +03:00
Sergey Matyunin b5863be168 Release v1.69.0-beta.5 candidate
Prepared the reviewed S8 work for the v1.69.0-beta.5 prerelease, refreshed the bilingual changelogs and release notes, and rebuilt the synchronized frontend bundles.

Issue: #373
Issue: #375
Issue: #376
Issue: #377
Issue: #378
User-Visible: yes
2026-08-29 23:14:59 +03:00
Sergey Matyunin 5f6ee65768 test: accept explicit value source golden
Reviewed by Claude in CODE-REVIEW-378-r2 against the complete Linux candidate. Exactly the new 42 percent value-face frame is accepted; 142 existing baselines retain their reviewed hashes.

Issue: #378
User-Visible: no
Release: v1.69.0-beta.5
Baseline-Reviewed: https://github.com/Matysh/houseplan-card/actions/runs/33271690654
2026-08-29 23:07:07 +03:00
32 changed files with 275 additions and 161 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ PLAN_ORPHAN_TTL_S = 3600
SCHEDULED_GRACE_S = 30 * 24 * 3600
FILES_DIR = "houseplan/files"
CONF_ADMIN_ONLY = "admin_only"
VERSION = "1.69.0-beta.4"
VERSION = "1.69.0-beta.5"
# Portable backup format. This is deliberately independent from the Home
# Assistant Store version above: storage migrations and files exported by a
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61",
"fingerprint": "c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-ClbNPLBK.js",
"houseplan-assets/houseplan-card-CyuodvSp.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 277941,
"initialViewGzipBytes": 277938,
"lazyFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/fr-wF02nOhD.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/fr-Wmhg31Dj.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
],
"lazyGzipBytes": 192271,
"lazyGzipBytes": 192247,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js"
],
"lazyEditorGzipBytes": 140225,
"lazyEditorGzipBytes": 140216,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
],
"lazyOnboardingGzipBytes": 9450,
"lazyOnboardingGzipBytes": 9444,
"lazyLocaleFiles": [
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/fr-wF02nOhD.js"
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/fr-Wmhg31Dj.js"
],
"lazyLocaleGzipBytes": 45552,
"lazyLocaleGzipBytes": 45538,
"files": [
{
"path": "houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"sha256": "fff25614b61ef0b0c99b6e7e859bc180e6c6c9c07e8c6f629a0aecc398d30c16",
"path": "houseplan-assets/backdrop-pick-V8akNul_.js",
"sha256": "4cc3fcf1bc098a01d9ef870badcf36dedee9ba4be1118b08ad83eef54e79779b",
"rawBytes": 6967,
"gzipBytes": 2956,
"gzipBytes": 2951,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BZOAaEAt.js",
"sha256": "f96555802b1dd563266cb91a2d60b6d929a78124c9b39de21ec57ecdf737b929",
"path": "houseplan-assets/de-BnfsQCZJ.js",
"sha256": "31b20098ba0421c0b816ee7ea0c48d0949f98b7f77314b923c3753c031cdc6b9",
"rawBytes": 75572,
"gzipBytes": 22996,
"gzipBytes": 22989,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-DGRuvanB.js",
"sha256": "b349264ac92b1202d1062529bb932905746fc4b452e936c843fa7530d0c42e3d",
"path": "houseplan-assets/editor-BkkWS-ED.js",
"sha256": "10047f3867c70e9043669906de59ad84fc758de5bd6bffdddc62a9946281997e",
"rawBytes": 3831,
"gzipBytes": 1584,
"gzipBytes": 1580,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-wF02nOhD.js",
"sha256": "a47ac2c6d4080c852e132aebe529af28e482b9a737abbb9a3494b98c4098df92",
"path": "houseplan-assets/fr-Wmhg31Dj.js",
"sha256": "23be1755bf6205ff1eaa82166a51d3dcdcb761b6ea5183f1c928fe3f5de2a4db",
"rawBytes": 77710,
"gzipBytes": 22556,
"gzipBytes": 22549,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-ClbNPLBK.js",
"sha256": "05b33313a780bc78aacc62028f9008d441632d5cc43b783ac500a4c1f15dce5e",
"path": "houseplan-assets/houseplan-card-CyuodvSp.js",
"sha256": "f47157cc4a17f79be4bba79cc23112cf19c761f844a526dfd8c7e4d5309103d4",
"rawBytes": 979430,
"gzipBytes": 277145,
"gzipBytes": 277146,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/fr-wF02nOhD.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/fr-Wmhg31Dj.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"sha256": "319278c617ae813077026c60a961c3e91163a8e64f0c0142a131d8b8a5205c6c",
"path": "houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"sha256": "28e5ddd4005a621135d9fa4ac6ed74c4c3c82b11ac80a879802a0c9cbee2c773",
"rawBytes": 526486,
"gzipBytes": 135685,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js",
"sha256": "4c76cf33d28962cef45156c6cd94a1f2818cc2010aa7d931c30978b31f4557b7",
"path": "houseplan-assets/houseplan-onboarding-runtime-CATDliha.js",
"sha256": "193da3d08a6eee74f1daaf1bb5f9d232228f8251c3b783606f833ba3eb35b778",
"rawBytes": 26195,
"gzipBytes": 6494,
"gzipBytes": 6493,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "47008127109b6f4b67935548e4513dccda7f7bff6a3c458e7b3fe310b0c89e49",
"sha256": "9fc2044a142b4c2017b947a504aa805dbbde4bab2fd7c8e0b257500ca2c935ef",
"rawBytes": 1183,
"gzipBytes": 796,
"gzipBytes": 792,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
}
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61";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-ClbNPLBK.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
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d";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-CyuodvSp.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}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61";try{await import("./houseplan-assets/houseplan-card-ClbNPLBK.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__="c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d";try{await import("./houseplan-assets/houseplan-card-CyuodvSp.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
@@ -16,5 +16,5 @@
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
"requirements": [],
"single_config_entry": true,
"version": "1.69.0-beta.4"
"version": "1.69.0-beta.5"
}
+5 -4
View File
@@ -1,11 +1,11 @@
{
"schema": 1,
"matrixVersion": 50,
"acceptedAt": "2026-08-29T09:39:46.951Z",
"sourceFingerprint": "f3809810087222d403b380d942a5162a92cae6e75be9ec5093b83c97472e5df6",
"matrixVersion": 51,
"acceptedAt": "2026-08-29T20:06:16.499Z",
"sourceFingerprint": "1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61",
"chromium": "151.0.7922.34",
"witnesses": {
"count": 128,
"count": 130,
"floor": 10
},
"scenarios": {
@@ -89,6 +89,7 @@
"openings-hidden-view-dark": "19ab4fd2ffce0fea0fe609b007a3e60f7abd84b40935cbf3c5fa9dc1fda21046",
"lighting-glow-sun-dark": "08e2964df6fd03abad4b9a81f1dbd0fe3812fb6b5f7908aeb027ad2953b2f88c",
"device-value-badge-positions-dark": "75ab0210d697f952b8a8977649e84308de3ce3b59c84ec93071179a1d6cb015d",
"device-value-face-cover-source-dark": "c48fa33e0a85164ee64bfce6dbc4127c96b4a1c065e4604310f1b16be144639d",
"device-icon-state-table-light": "85c785824ccafc2d5f50e834ad2d746319d64ecb292d1b9cf7fab8661d5d410d",
"device-icon-state-table-dark": "03a90523132c3dbe6c78d44d01fe89df93e2d0474f91ef84e7107e6e4bc7d337",
"device-text-shell-long-light": "97a113b5164f373b4af2922c8e71bc1f063d8c2761b72c3182697fcb1aac3cab",
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

+54 -54
View File
@@ -1,125 +1,125 @@
{
"schema": 1,
"fingerprint": "1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61",
"fingerprint": "c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d",
"entry": "houseplan-card.js",
"initialViewFiles": [
"houseplan-assets/houseplan-card-ClbNPLBK.js",
"houseplan-assets/houseplan-card-CyuodvSp.js",
"houseplan-card.js"
],
"initialViewGzipBytes": 277941,
"initialViewGzipBytes": 277938,
"lazyFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/fr-wF02nOhD.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/fr-Wmhg31Dj.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
],
"lazyGzipBytes": 192271,
"lazyGzipBytes": 192247,
"lazyEditorFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js"
],
"lazyEditorGzipBytes": 140225,
"lazyEditorGzipBytes": 140216,
"lazyOnboardingFiles": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
],
"lazyOnboardingGzipBytes": 9450,
"lazyOnboardingGzipBytes": 9444,
"lazyLocaleFiles": [
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/fr-wF02nOhD.js"
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/fr-Wmhg31Dj.js"
],
"lazyLocaleGzipBytes": 45552,
"lazyLocaleGzipBytes": 45538,
"files": [
{
"path": "houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"sha256": "fff25614b61ef0b0c99b6e7e859bc180e6c6c9c07e8c6f629a0aecc398d30c16",
"path": "houseplan-assets/backdrop-pick-V8akNul_.js",
"sha256": "4cc3fcf1bc098a01d9ef870badcf36dedee9ba4be1118b08ad83eef54e79779b",
"rawBytes": 6967,
"gzipBytes": 2956,
"gzipBytes": 2951,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/de-BZOAaEAt.js",
"sha256": "f96555802b1dd563266cb91a2d60b6d929a78124c9b39de21ec57ecdf737b929",
"path": "houseplan-assets/de-BnfsQCZJ.js",
"sha256": "31b20098ba0421c0b816ee7ea0c48d0949f98b7f77314b923c3753c031cdc6b9",
"rawBytes": 75572,
"gzipBytes": 22996,
"gzipBytes": 22989,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/editor-DGRuvanB.js",
"sha256": "b349264ac92b1202d1062529bb932905746fc4b452e936c843fa7530d0c42e3d",
"path": "houseplan-assets/editor-BkkWS-ED.js",
"sha256": "10047f3867c70e9043669906de59ad84fc758de5bd6bffdddc62a9946281997e",
"rawBytes": 3831,
"gzipBytes": 1584,
"gzipBytes": 1580,
"isEntry": false,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/fr-wF02nOhD.js",
"sha256": "a47ac2c6d4080c852e132aebe529af28e482b9a737abbb9a3494b98c4098df92",
"path": "houseplan-assets/fr-Wmhg31Dj.js",
"sha256": "23be1755bf6205ff1eaa82166a51d3dcdcb761b6ea5183f1c928fe3f5de2a4db",
"rawBytes": 77710,
"gzipBytes": 22556,
"gzipBytes": 22549,
"isEntry": false,
"imports": [],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-card-ClbNPLBK.js",
"sha256": "05b33313a780bc78aacc62028f9008d441632d5cc43b783ac500a4c1f15dce5e",
"path": "houseplan-assets/houseplan-card-CyuodvSp.js",
"sha256": "f47157cc4a17f79be4bba79cc23112cf19c761f844a526dfd8c7e4d5309103d4",
"rawBytes": 979430,
"gzipBytes": 277145,
"gzipBytes": 277146,
"isEntry": false,
"imports": [],
"dynamicImports": [
"houseplan-assets/de-BZOAaEAt.js",
"houseplan-assets/editor-DGRuvanB.js",
"houseplan-assets/fr-wF02nOhD.js",
"houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js"
"houseplan-assets/de-BnfsQCZJ.js",
"houseplan-assets/editor-BkkWS-ED.js",
"houseplan-assets/fr-Wmhg31Dj.js",
"houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"houseplan-assets/houseplan-onboarding-runtime-CATDliha.js"
]
},
{
"path": "houseplan-assets/houseplan-editor-runtime-CtEBnmPW.js",
"sha256": "319278c617ae813077026c60a961c3e91163a8e64f0c0142a131d8b8a5205c6c",
"path": "houseplan-assets/houseplan-editor-runtime-DPoPwoYP.js",
"sha256": "28e5ddd4005a621135d9fa4ac6ed74c4c3c82b11ac80a879802a0c9cbee2c773",
"rawBytes": 526486,
"gzipBytes": 135685,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-assets/houseplan-onboarding-runtime-BChyRw3O.js",
"sha256": "4c76cf33d28962cef45156c6cd94a1f2818cc2010aa7d931c30978b31f4557b7",
"path": "houseplan-assets/houseplan-onboarding-runtime-CATDliha.js",
"sha256": "193da3d08a6eee74f1daaf1bb5f9d232228f8251c3b783606f833ba3eb35b778",
"rawBytes": 26195,
"gzipBytes": 6494,
"gzipBytes": 6493,
"isEntry": false,
"imports": [
"houseplan-assets/backdrop-pick-Ara0Hw0d.js",
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/backdrop-pick-V8akNul_.js",
"houseplan-assets/houseplan-card-CyuodvSp.js"
],
"dynamicImports": []
},
{
"path": "houseplan-card.js",
"sha256": "47008127109b6f4b67935548e4513dccda7f7bff6a3c458e7b3fe310b0c89e49",
"sha256": "9fc2044a142b4c2017b947a504aa805dbbde4bab2fd7c8e0b257500ca2c935ef",
"rawBytes": 1183,
"gzipBytes": 796,
"gzipBytes": 792,
"isEntry": true,
"imports": [
"houseplan-assets/houseplan-card-ClbNPLBK.js"
"houseplan-assets/houseplan-card-CyuodvSp.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,4 +1,4 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61";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-ClbNPLBK.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
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d";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-CyuodvSp.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}
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 -1
View File
@@ -1 +1 @@
globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="1c0a2661ccd125f4f23faab4730974a20d7b63ace15c3be781c5bbe602843d61";try{await import("./houseplan-assets/houseplan-card-ClbNPLBK.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__="c49aea67344ecbd52d1b1cd83dee9c815333cc3d295db2ed3fca6d9cf54a832d";try{await import("./houseplan-assets/houseplan-card-CyuodvSp.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)}
+2
View File
@@ -2,6 +2,8 @@
## Unreleased
## v1.69.0-beta.5 — 2026-08-29
- **Value + state** can now show the exact reading the user needs instead of
relying only on the automatically chosen entity state. The Device editor
offers the same states, useful attributes, LQI and linked-light values as
+2
View File
@@ -8,6 +8,8 @@
## Не выпущено
## v1.69.0-beta.5 — 2026-08-29
- В режиме **«Значение + состояние»** теперь можно выбрать нужный показатель,
а не полагаться только на автоматически выбранное состояние сущности.
Редактор устройства предлагает те же состояния, полезные атрибуты, LQI и
+9 -9
View File
@@ -1,18 +1,18 @@
<!-- release: v1.69.0-beta.4 -->
<!-- release: v1.69.0-beta.5 -->
## Основное
- Фирменный Glow со светом через проёмы и тенями от стен теперь можно включить и в отдельной `houseplan-space-card`; пустой заголовок делает её действительно компактной ([#374](https://github.com/Matysh/houseplan-card/issues/374), [#372](https://github.com/Matysh/houseplan-card/issues/372)).
- Добавлен полный французский перевод сообщества с автоматическим выбором для французских профилей Home Assistant ([#371](https://github.com/Matysh/houseplan-card/issues/371)).
- Редактор подложки стал безопаснее и быстрее: большие изображения можно уменьшить до загрузки, устройства не мешают рисованию, а основной цвет новых элементов всегда доступен на панели ([#39](https://github.com/Matysh/houseplan-card/issues/39), [#360](https://github.com/Matysh/houseplan-card/issues/360), [#362](https://github.com/Matysh/houseplan-card/issues/362)).
- `houseplan-space-card` получила плотное кадрирование по конструкции дома, а её Glow больше не пересобирает сцену с нуля на каждый тик Home Assistant ([#373](https://github.com/Matysh/houseplan-card/issues/373), [#375](https://github.com/Matysh/houseplan-card/issues/375)).
- Цвет и стиль по умолчанию для новых элементов подложки теперь сохраняются вместе с планом и переживают перезагрузку ([#377](https://github.com/Matysh/houseplan-card/issues/377)).
- В режиме «Значение + состояние» можно выбрать точный источник показания — например, позицию шторы `42 %` вместо её текстового состояния ([#378](https://github.com/Matysh/houseplan-card/issues/378)).
- Мелкие исправления и улучшения.
## Highlights
- The signature Glow with light through openings and wall shadows can now be enabled in the standalone `houseplan-space-card`; an empty title also makes the card genuinely compact ([#374](https://github.com/Matysh/houseplan-card/issues/374), [#372](https://github.com/Matysh/houseplan-card/issues/372)).
- A complete community French translation is available and selected automatically for French Home Assistant profiles ([#371](https://github.com/Matysh/houseplan-card/issues/371)).
- The Background editor is safer and faster: huge images can be reduced before loading, device markers no longer obstruct drawing, and the main colour for new objects stays on the toolbar ([#39](https://github.com/Matysh/houseplan-card/issues/39), [#360](https://github.com/Matysh/houseplan-card/issues/360), [#362](https://github.com/Matysh/houseplan-card/issues/362)).
- `houseplan-space-card` now offers tight whole-house framing, while its Glow no longer rebuilds the scene from scratch on every Home Assistant tick ([#373](https://github.com/Matysh/houseplan-card/issues/373), [#375](https://github.com/Matysh/houseplan-card/issues/375)).
- The default colour and style for new Background objects are now stored with the plan and survive a reload ([#377](https://github.com/Matysh/houseplan-card/issues/377)).
- **Value + state** can use an exact reading source — for example, a cover position of `42 %` instead of its textual state ([#378](https://github.com/Matysh/houseplan-card/issues/378)).
- Small fixes and improvements.
[Полный список изменений на русском](https://github.com/Matysh/houseplan-card/blob/v1.69.0-beta.4/docs/CHANGELOG.ru.md)
· [Full changelog in English](https://github.com/Matysh/houseplan-card/blob/v1.69.0-beta.4/docs/CHANGELOG.md)
[Полный список изменений на русском](https://github.com/Matysh/houseplan-card/blob/v1.69.0-beta.5/docs/CHANGELOG.ru.md)
· [Full changelog in English](https://github.com/Matysh/houseplan-card/blob/v1.69.0-beta.5/docs/CHANGELOG.md)
+2 -2
View File
@@ -21,8 +21,8 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
| Item | State |
|---|---|
| Version | **v1.69.0-beta.4 candidate** everywhere (manifest, const.py, package.json, CARD_VERSION) |
| Current local cycle | v1.68.1 is the current stable release. v1.69.0-beta.4 packages the reviewed `S8-merged` work since beta.3: opt-in Glow and compact empty-title framing in `houseplan-space-card` (#374, #372), French localisation (#371), safer Background editing and large-backdrop handling (#39, #360–#363), bounded moving-opening Glow updates (#366), furniture stroke scaling (#361), and the audit fixes in #369. Publication remains gated by exact-SHA Validate. |
| Version | **v1.69.0-beta.5 candidate** everywhere (manifest, const.py, package.json, CARD_VERSION) |
| Current local cycle | v1.68.1 is the current stable release. v1.69.0-beta.5 packages the reviewed `S8-merged` work since beta.4: tighter whole-house framing and cached Glow in `houseplan-space-card` (#373, #375), persistent Background default style (#377), the beta.4 audit polish (#376), and an explicit source selector for the Value + state face (#378). Publication remains gated by exact-SHA Validate. |
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 Stage 2 ships in v1.64.0 and evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation remains a separate task. |
| Workflow | Superseded 2026-08-12: the pre-1.62 rule of "local edits without tests or commits" is **dead** — since release 1.62 every product change follows `PROCESS.md` (issue in `S5-ready`+, branch `issue/<NN>-slug`, trailers on every commit, review pipeline; `AGENTS.md` is the summary). Release mechanics below remain current. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first); every bullet links its GitHub issue (#NN) so the #328 rules stay machine-checkable. A STABLE body aggregates the changelog since the PREVIOUS STABLE release (never since the last beta): features/fixes described across the line's beta changelogs must appear, while bugs that were introduced and fixed strictly inside the beta line (never shipped in any stable) are excluded — draft with `npm run release:notes -- <tag>`, curate by hand, then `npm run release:notes -- <tag> --verify` must pass. `Мелкие исправления и улучшения` / `Small fixes and improvements` is allowed only when the range really contains user-visible work not itemised in the body; a single-issue hotfix ships without it (the verifier enforces this). Every body ends with separate links to the Russian and English changelogs. Open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records; their labels carry priority and workflow status (`PROCESS.md` §9). GitHub Projects is no longer used. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "houseplan-card",
"version": "1.69.0-beta.4",
"version": "1.69.0-beta.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "houseplan-card",
"version": "1.69.0-beta.4",
"version": "1.69.0-beta.5",
"license": "MIT",
"dependencies": {
"lit": "^3.1.3",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "houseplan-card",
"version": "1.69.0-beta.4",
"version": "1.69.0-beta.5",
"description": "Interactive house plan Lovelace card for Home Assistant",
"license": "MIT",
"type": "module",
+63 -6
View File
@@ -106,7 +106,10 @@ export const FS = '\x1f';
export const RS = '\x1e';
export const LOG_FORMAT = `%H${FS}%s${FS}%aI${FS}%b${RS}`;
export function makeCommit({ sha = '', subject = '', body = '', files = [], authorDate = '' }) {
export function makeCommit({
sha = '', subject = '', body = '', files = [], authorDate = '',
releaseSourceViolations = null,
}) {
const text = `${subject}\n${body}`;
const all = (name) =>
[...text.matchAll(new RegExp(`^${name}:\\s*(.+)$`, 'gmi'))].map((m) => m[1].trim());
@@ -123,6 +126,9 @@ export function makeCommit({ sha = '', subject = '', body = '', files = [], auth
release: one('Release'),
baselineReviewed: one('Baseline-Reviewed'),
gates: one('Gates'),
// null = вызывающий не доказал содержимое diff. Для stable release это
// намеренно fail-closed: одного имени разрешённого version source мало.
releaseSourceViolations,
// Кандидат беты несёт работу и живёт по общим правилам — решение 1.
isRelease:
(/^Release v\d/.test(subject) && !/-(beta|rc|alpha)\.|candidate/i.test(subject))
@@ -130,7 +136,9 @@ export function makeCommit({ sha = '', subject = '', body = '', files = [], auth
};
}
export function parseRecords(raw, filesOf = () => []) {
export function parseRecords(
raw, filesOf = () => [], releaseSourceViolationsOf = () => null,
) {
if (!raw.trim()) return [];
return raw
.split(RS)
@@ -138,10 +146,42 @@ export function parseRecords(raw, filesOf = () => []) {
.filter((r) => r.trim())
.map((rec) => {
const [sha, subject, authorDate = '', body = ''] = rec.split(FS);
return makeCommit({ sha, subject, body, files: filesOf(sha), authorDate });
const files = filesOf(sha);
return makeCommit({
sha, subject, body, files, authorDate,
releaseSourceViolations: releaseSourceViolationsOf(sha, files),
});
});
}
const RELEASE_VERSION_DECLARATIONS = new Map([
['src/houseplan-card.ts', /^const CARD_VERSION = '[^'\r\n]+';$/gm],
['src/houseplan-editor-runtime.ts', /^const CARD_VERSION = '[^'\r\n]+';$/gm],
['custom_components/houseplan/const.py', /^VERSION = "[^"\r\n]+"$/gm],
]);
// Stable promotion действительно обязан менять эти три строки: они входят в
// шесть канонических version sources (§9.3). Сравнение целого blob до/после с
// нормализованной декларацией доказывает, что под видом bump не проехало ни
// одного другого изменения продукта. Ровно одно совпадение с обеих сторон —
// часть доказательства; неоднозначный или недоступный diff остаётся fail-closed.
export function isReleaseVersionOnlyChange(path, before, after) {
const pattern = RELEASE_VERSION_DECLARATIONS.get(path);
if (!pattern || typeof before !== 'string' || typeof after !== 'string') return false;
const normalize = (source) => {
let count = 0;
pattern.lastIndex = 0;
const normalized = source.replace(pattern, () => {
count += 1;
return '__HOUSEPLAN_RELEASE_VERSION__';
});
return { count, normalized };
};
const left = normalize(before);
const right = normalize(after);
return left.count === 1 && right.count === 1 && left.normalized === right.normalized;
}
// --- проверки по одному коммиту: 1, 4, 5, 6, 9 ---
export function evaluateCommit(c) {
const out = [];
@@ -159,8 +199,11 @@ export function evaluateCommit(c) {
if (!c.release) {
fail(5, `релизный коммит «${c.subject.slice(0, 50)}» без трейлера «Release: vX.Y.Z»`);
}
if (sources.length) {
fail(6, `релизный коммит содержит продуктовый исходник: ${sources.slice(0, 3).join(', ')}`);
const violations = Array.isArray(c.releaseSourceViolations)
? c.releaseSourceViolations
: sources;
if (violations.length) {
fail(6, `релизный коммит содержит не-версионное изменение продукта: ${violations.slice(0, 3).join(', ')}`);
}
if ((c.gates ?? '').toLowerCase() === 'light') {
fail(9, '«Gates: light» на релизном коммите запрещён');
@@ -610,8 +653,21 @@ function main(argv) {
const filesOf = (sha) =>
git(['show', '--name-only', '--pretty=format:', sha], repo)
.split('\n').map((s) => s.trim()).filter(Boolean);
const blobOf = (revision, path) => {
const r = spawnSync('git', ['-C', repo, 'show', `${revision}:${path}`], {
encoding: 'utf8', maxBuffer: 64 * 1024 * 1024,
});
return r.status === 0 ? r.stdout : null;
};
const releaseSourceViolationsOf = (sha, files) => files
.filter((file) => /^src\//.test(file) || /^custom_components\/houseplan\/.*\.py$/.test(file))
.filter((file) => !isReleaseVersionOnlyChange(
file, blobOf(`${sha}^`, file), blobOf(sha, file),
));
const commits = parseRecords(
git(['log', '--reverse', `--pretty=format:${LOG_FORMAT}`, range], repo), filesOf,
git(['log', '--reverse', `--pretty=format:${LOG_FORMAT}`, range], repo),
filesOf,
releaseSourceViolationsOf,
);
const branch = git(['rev-parse', '--abbrev-ref', 'HEAD'], repo).trim();
@@ -649,6 +705,7 @@ function main(argv) {
const own = parseRecords(
git(['log', '--reverse', `--pretty=format:${LOG_FORMAT}`, 'origin/dev..HEAD'], repo),
filesOf,
releaseSourceViolationsOf,
);
return commitsNeedingTargetValidation(own, { targetRef, isCommitOnMain });
})()
+1 -1
View File
@@ -347,7 +347,7 @@ import {
} from './space-order';
import { applyOpeningMoves, mergeCollinearPartitions, spaceMergeGeometry } from './wall-merge';
const CARD_VERSION = '1.69.0-beta.4';
const CARD_VERSION = '1.69.0-beta.5';
const ENTRY_BUILD_FINGERPRINT = '__HOUSEPLAN_SOURCE_FINGERPRINT__';
const EDITOR_RETRY_ASSET = '__HOUSEPLAN_EDITOR_RETRY_ASSET__';
+1 -1
View File
@@ -337,7 +337,7 @@ import {
} from './space-order';
import { applyOpeningMoves, mergeCollinearPartitions, spaceMergeGeometry } from './wall-merge';
const CARD_VERSION = '1.69.0-beta.4';
const CARD_VERSION = '1.69.0-beta.5';
type ResizeLiveLabel = {
kind: 'length';
+53 -1
View File
@@ -24,6 +24,7 @@ import {
commitsUnderRuleOne,
evaluateCommit,
isInfrastructureRange,
isReleaseVersionOnlyChange,
makeCommit,
parseRecords,
FS,
@@ -113,6 +114,35 @@ test('a release commit carrying product source fails rule 6', () => {
assert.deepEqual(rules(evaluateCommit(bad)), [6]);
});
test('a release commit allows only proven canonical version declarations', () => {
const path = 'src/houseplan-card.ts';
const before = "const CARD_VERSION = '1.69.0-beta.5';\nexport const value = 1;\n";
const after = "const CARD_VERSION = '1.69.0';\nexport const value = 1;\n";
assert.equal(isReleaseVersionOnlyChange(path, before, after), true);
assert.equal(isReleaseVersionOnlyChange(
path, before, "const CARD_VERSION = '1.69.0';\nexport const value = 2;\n",
), false);
assert.equal(isReleaseVersionOnlyChange('src/another.ts', before, after), false);
const stable = makeCommit({
sha: 'deadbeefcafe',
subject: 'Release v1.69.0',
body: 'Release: v1.69.0',
files: [path, 'src/houseplan-editor-runtime.ts', 'custom_components/houseplan/const.py'],
releaseSourceViolations: [],
});
assert.deepEqual(rules(evaluateCommit(stable)), []);
const mixed = makeCommit({
sha: 'deadbeefcafe',
subject: 'Release v1.69.0',
body: 'Release: v1.69.0',
files: [path, 'src/houseplan-editor-runtime.ts', 'custom_components/houseplan/const.py'],
releaseSourceViolations: ['src/houseplan-card.ts'],
});
assert.deepEqual(rules(evaluateCommit(mixed)), [6]);
});
test('Gates: light is refused on release and generated commits', () => {
assert.deepEqual(
rules(evaluateCommit(commit('Release v1.62.0', 'Release: v1.62.0\nGates: light', ['dist/a.js']))),
@@ -390,6 +420,28 @@ test('the CLI exits 0 on a clean range and 1 on a broken one', (t) => {
assert.equal(broken.status, 1, broken.stdout + broken.stderr);
assert.match(broken.stdout, /FAIL п\.1/);
// Stable promotion меняет канонические строки версии внутри исходников,
// но не несёт никакого другого продуктового diff.
git('checkout', '-q', 'dev');
git('reset', '-q', '--hard', base);
write('src/houseplan-card.ts', "const CARD_VERSION = '1.69.0-beta.5';\nexport const a = 1;\n");
write('src/houseplan-editor-runtime.ts', "const CARD_VERSION = '1.69.0-beta.5';\nexport const b = 1;\n");
write('custom_components/houseplan/const.py', 'VERSION = "1.69.0-beta.5"\nVALUE = 1\n');
commitAll('Prerelease tree\n\nIssue: #1\nUser-Visible: no');
const prerelease = git('rev-parse', 'HEAD').trim();
write('src/houseplan-card.ts', "const CARD_VERSION = '1.69.0';\nexport const a = 1;\n");
write('src/houseplan-editor-runtime.ts', "const CARD_VERSION = '1.69.0';\nexport const b = 1;\n");
write('custom_components/houseplan/const.py', 'VERSION = "1.69.0"\nVALUE = 1\n');
commitAll('Release v1.69.0\n\nRelease: v1.69.0\nUser-Visible: yes');
const stable = runGate(`${prerelease}..HEAD`);
assert.equal(stable.status, 0, stable.stdout + stable.stderr);
write('src/houseplan-card.ts', "const CARD_VERSION = '1.69.1';\nexport const a = 2;\n");
commitAll('Release v1.69.1\n\nRelease: v1.69.1\nUser-Visible: yes');
const polluted = runGate('HEAD^..HEAD');
assert.equal(polluted.status, 1, polluted.stdout + polluted.stderr);
assert.match(polluted.stdout, /FAIL п\.6/);
// --report печатает то же, но не краснеет.
const report = spawnSync(process.execPath,
[gate, '--repo', dir, '--range', `${base}..HEAD`, '--report'], { encoding: 'utf8' });
@@ -399,7 +451,7 @@ test('the CLI exits 0 on a clean range and 1 on a broken one', (t) => {
[gate, '--repo', dir, '--range', `${base}..HEAD`, '--json'], { encoding: 'utf8' });
const parsed = JSON.parse(asJson.stdout);
assert.equal(parsed.ok, false);
assert.equal(parsed.commits, 2);
assert.equal(parsed.commits, 3);
// Проверка 2 судит только коммиты самой ветки. Диапазон из события CI шире:
// после ребейза merge-base уезжает назад и втягивает коммиты dev с чужими