mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
v1.59.0-beta.8: audit follow-ups and inner-corner sun rays
This commit is contained in:
@@ -42,9 +42,9 @@ const BUNDLE_VERSION = m?.[1] ?? (m?.[2] && bundle.match(new RegExp(`[^\\w$]${m[
|
||||
check('bundleVersionFound', typeof BUNDLE_VERSION === 'string' && BUNDLE_VERSION.length > 0);
|
||||
// значения зафиксированы прогоном на v1.43.1 и сверены с кодом (audit T1)
|
||||
checkAll(res, {
|
||||
"rows": 15, // 10 цветов + радиус свечения + режим фона + цвет фона + погода (docs/SUN.md)
|
||||
// + «Выровнять всё по сетке» (docs/CANVAS.md §9)
|
||||
"groups": ["Fill: lights", "Fill: temperature", "Fill: zigbee signal", "Light-sources fill", "Stage background", "Sun", "Grid", "About"],
|
||||
"rows": 16, // 11 цветов (включая wall_fill) + радиус свечения + фон + погода (docs/SUN.md)
|
||||
// + «Выровнять всё по сетке» (docs/CANVAS.md §9)
|
||||
"groups": ["Fill: lights", "Fill: temperature", "Fill: zigbee signal", "Light-sources fill", "Walls", "Stage background", "Sun", "Grid", "About"],
|
||||
"aboutVersion": `Houseplan Card v${BUNDLE_VERSION}`, // та же константа, что в баннере
|
||||
"aboutLinks": [
|
||||
{ "href": "https://github.com/Matysh/houseplan-card", "target": "_blank", "rel": "noopener" },
|
||||
|
||||
@@ -68,6 +68,35 @@ out.bigKeepsLiving = s.rooms.some(r=>r.id==='r1' && r.area==='living_room');
|
||||
out.newRoom = s.rooms.find(r=>!['r1','r2','r3','r4','rg'].includes(r.id))?.name;
|
||||
await restore();
|
||||
|
||||
// AUD-159B7-01: one open span crossing the Split point belongs to two room
|
||||
// pairs afterwards. The card transaction must persist both pieces and derive
|
||||
// light-zone connectivity for both children, not keep only the first half.
|
||||
const spanSplit = await page.evaluate(() => {
|
||||
const c = window.__card;
|
||||
const sp = c._serverCfg.spaces.find((x) => x.id === 'f1');
|
||||
sp.rooms = [
|
||||
{ id: 'left_top', name: 'Left top', area: null, poly: [[0, 0], [.5, 0], [.5, .2], [0, .2]] },
|
||||
{ id: 'left_bottom', name: 'Left bottom', area: null, poly: [[0, .2], [.5, .2], [.5, .4], [0, .4]] },
|
||||
{ id: 'right', name: 'Right', area: null, poly: [[.5, 0], [1, 0], [1, .4], [.5, .4]] },
|
||||
];
|
||||
sp.open_spans = [{ a: [.5, .1], b: [.5, .3] }];
|
||||
c._commitOpenSpans();
|
||||
const pieces = (sp.open_spans || []).map((e) =>
|
||||
[Math.min(e.a[1], e.b[1]), Math.max(e.a[1], e.b[1])]);
|
||||
const links = Object.fromEntries(sp.rooms.map((r) => [r.id, r.open_to || []]));
|
||||
return { pieces, links };
|
||||
});
|
||||
out.splitKeepsOpenPieces = spanSplit.pieces.length === 2
|
||||
&& Math.abs(spanSplit.pieces[0][0] - .1) < 1e-9
|
||||
&& Math.abs(spanSplit.pieces[0][1] - .2) < 1e-9
|
||||
&& Math.abs(spanSplit.pieces[1][0] - .2) < 1e-9
|
||||
&& Math.abs(spanSplit.pieces[1][1] - .3) < 1e-9;
|
||||
out.splitSyncsBothSides = spanSplit.links.left_top?.includes('right')
|
||||
&& spanSplit.links.left_bottom?.includes('right')
|
||||
&& spanSplit.links.right?.includes('left_top')
|
||||
&& spanSplit.links.right?.includes('left_bottom');
|
||||
await restore();
|
||||
|
||||
// SPLIT along a wall → refused (both pts on top wall of rg)
|
||||
await page.evaluate(()=>{const c=window.__card; c._serverCfg.spaces.find(s=>s.id==='f1').rooms.push(
|
||||
{id:'rg2', name:'G2', area:null, poly:[[0.05,0.0625],[0.5,0.0625],[0.5,0.5],[0.05,0.5]]});
|
||||
|
||||
@@ -70,6 +70,30 @@ const res = await page.evaluate(async () => {
|
||||
const areaAfter = floorAreaOf('r1');
|
||||
out.areaShrinks = areaAfter > 0 && areaAfter < areaBefore - 1;
|
||||
|
||||
// A sun shaft starts at the two room-side corners of a thick opening, not
|
||||
// on the wall centreline and not at an incidence-narrowed pseudo-aperture.
|
||||
sp().openings = [{
|
||||
id: 'wtSun', type: 'window', x: 0.04, y: 0.25, angle: 90, length: 0.09,
|
||||
}];
|
||||
sp().settings = { ...(sp().settings || {}), north_deg: 0, sun_rays: true };
|
||||
c.hass = { ...c.hass, states: { ...c.hass.states, 'sun.sun': {
|
||||
entity_id: 'sun.sun', state: 'above_horizon',
|
||||
attributes: { azimuth: 240, elevation: 60 },
|
||||
} } };
|
||||
c._setMode('view');
|
||||
await upd();
|
||||
const sunRay = (c._sunRaysCache?.rays || []).find((r) => r.openingId === 'wtSun');
|
||||
const sunDepth = (20 / c._cellCm) * c._gridPitch;
|
||||
const ys = sunRay ? [sunRay.a[1], sunRay.b[1]].sort((a, b) => a - b) : [];
|
||||
out.sunStartsInnerFace = !!sunRay
|
||||
&& Math.abs(sunRay.a[0] - (40 + sunDepth / 2)) < 1e-6
|
||||
&& Math.abs(sunRay.b[0] - (40 + sunDepth / 2)) < 1e-6;
|
||||
out.sunStartsInnerCorners = !!sunRay
|
||||
&& Math.abs(ys[0] - 205) < 1e-6 && Math.abs(ys[1] - 295) < 1e-6;
|
||||
c._setMode('plan');
|
||||
c._tool = 'wallthick';
|
||||
await upd();
|
||||
|
||||
c._tool = 'wallthick';
|
||||
c._wallThickClick([550, 250]);
|
||||
await upd();
|
||||
@@ -201,6 +225,47 @@ const res = await page.evaluate(async () => {
|
||||
}
|
||||
|
||||
out.pixelProbe = !!sr().querySelector('.wallbody');
|
||||
|
||||
// AUD-159B7-04: the same screen-depth policy owns both cards. At 1 cm the
|
||||
// hatch must be suppressed in full and static views; at 20 cm both restore
|
||||
// it. The static card measures its actual stage width via ResizeObserver.
|
||||
c._setMode('view');
|
||||
const parityCfg = JSON.parse(JSON.stringify(c._serverCfg));
|
||||
const paritySp = parityCfg.spaces.find((x) => x.id === c._space);
|
||||
paritySp.settings = { ...(paritySp.settings || {}), show_borders: true };
|
||||
paritySp.walls = (paritySp.walls || []).map((w) => ({ ...w, cm: 1 }));
|
||||
c._serverCfg = parityCfg;
|
||||
await upd();
|
||||
|
||||
await customElements.whenDefined('houseplan-space-card');
|
||||
const baseCall = c.hass.callWS.bind(c.hass);
|
||||
const staticHass = { ...c.hass, callWS: async (m) => {
|
||||
if (m.type === 'houseplan/config/get') return { config: parityCfg, rev: 1 };
|
||||
if (m.type === 'houseplan/layout/get') return { layout: c._layout || {}, rev: 1 };
|
||||
return baseCall(m);
|
||||
} };
|
||||
const staticCard = document.createElement('houseplan-space-card');
|
||||
staticCard.setConfig({ type: 'custom:houseplan-space-card', space: c._space, show_button: false });
|
||||
staticCard.hass = staticHass;
|
||||
document.body.appendChild(staticCard);
|
||||
const t0 = Date.now();
|
||||
while ((!staticCard.renderRoot?.querySelector('.wallbody') || !(staticCard._stageWidth > 0))
|
||||
&& Date.now() - t0 < 6000) {
|
||||
await new Promise((r) => setTimeout(r, 60));
|
||||
}
|
||||
await staticCard.updateComplete;
|
||||
out.thinWallFullSolid = !!sr().querySelector('.wallbody.solid');
|
||||
out.thinWallStaticSolid = !!staticCard.renderRoot?.querySelector('.wallbody.solid');
|
||||
|
||||
for (const w of paritySp.walls || []) w.cm = 20;
|
||||
c._cfgEpoch++;
|
||||
c.requestUpdate();
|
||||
staticCard.requestUpdate();
|
||||
await c.updateComplete;
|
||||
await staticCard.updateComplete;
|
||||
out.thickWallFullHatched = !!sr().querySelector('.wallbody:not(.solid)');
|
||||
out.thickWallStaticHatched = !!staticCard.renderRoot?.querySelector('.wallbody:not(.solid)');
|
||||
staticCard.remove();
|
||||
return out;
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user