Files
houseplan-card/demo/smoke_room_climate_placement.mjs
2026-08-28 16:48:10 +03:00

113 lines
4.8 KiB
JavaScript

// Issue #317: a real sensor explicitly placed into a House Plan room owns the
// automatic room-climate vote, including when that room has no HA Area. The
// same frame feeds label, tooltip, temperature fill and hosted Static.
import { launch, checkAll, finish } from './serve.mjs';
const { page, browser } = await launch();
const out = await page.evaluate(async () => {
const result = {};
const card = window.__card;
const root = () => card.shadowRoot || card.renderRoot;
const baseHass = window.__mkHass();
const localRoom = {
id: 'local-climate', name: 'Local climate', area: null,
poly: [[0.08, 0.62], [0.38, 0.62], [0.38, 0.82], [0.08, 0.82]],
};
const config = JSON.parse(JSON.stringify(card._serverCfg));
const floor = config.spaces.find((space) => space.id === 'f1');
floor.rooms.push(localRoom);
floor.settings = {
...(floor.settings || {}), show_names: true, label_temp: true,
label_hum: true, fill_mode: 'temp',
};
config.markers = [
...(config.markers || []).filter((marker) => marker.binding !== 'device:d_temp'),
{
id: 'd_temp', binding: 'device:d_temp', space: 'f1', area: null,
room_id: localRoom.id, hidden: true,
},
];
card._serverCfg = config;
card._cfgEpoch++;
card._regSignature = '';
card._maybeRebuildDevices();
card.hass = { ...baseHass };
card.requestUpdate();
await card.updateComplete;
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
const localKey = '@room/f1/local-climate';
result.localAutomatic = card._climate().get(localKey)?.temp === 22.4;
result.registryRoomReleased = card._climate().get('living_room')?.temp == null;
const localLabel = root().querySelector('[data-hp="room-label"][data-id="local-climate"]');
result.labelShowsAutomatic = localLabel?.textContent.includes('22.4°') === true;
result.oldLabelDropsMovedValue = root()
.querySelector('[data-hp="room-label"][data-id="r1"]')?.textContent.includes('22.4°') === false;
const localShape = root().querySelector('[data-hp="room"][data-id="local-climate"]');
const oldShape = root().querySelector('[data-hp="room"][data-id="r1"]');
result.localTempFill = localShape?.classList.contains('filled') === true;
result.oldTempFillRemoved = oldShape?.classList.contains('filled') === false;
localShape?.dispatchEvent(new PointerEvent('pointermove', {
bubbles: true, pointerType: 'mouse', clientX: 120, clientY: 180,
}));
await card.updateComplete;
result.tooltipSharesAutomatic = card._tip?.title === 'Local climate'
&& card._tip?.temp === 22.4;
// Hosted Static receives the same config and active HA snapshot. It has no
// tooltip/metric row by design, but its temperature fill must use the same
// room target rather than the old visible-device/HA-Area helper.
await customElements.whenDefined('houseplan-space-card');
const staticHass = {
...baseHass,
callWS: async (message) => {
if (message.type === 'houseplan/config/get') return { config, rev: 317, can_write: true };
if (message.type === 'houseplan/layout/get') return { layout: {}, rev: 317 };
return baseHass.callWS(message);
},
};
const staticCard = document.createElement('houseplan-space-card');
staticCard.setConfig({ type: 'custom:houseplan-space-card', space: 'f1', show_button: false });
staticCard.hass = staticHass;
document.body.appendChild(staticCard);
const deadline = Date.now() + 6000;
while (!staticCard.renderRoot?.querySelector('[data-hp="room"][data-id="local-climate"]')
&& Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 60));
await staticCard.updateComplete;
const staticRoot = staticCard.shadowRoot || staticCard.renderRoot;
result.staticLocalTempFill = staticRoot
?.querySelector('[data-hp="room"][data-id="local-climate"]')
?.classList.contains('filled') === true;
result.staticOldFillRemoved = staticRoot
?.querySelector('[data-hp="room"][data-id="r1"]')
?.classList.contains('filled') === false;
staticCard.remove();
// A configured source remains an override, not another vote or a fallback.
const configuredRoom = floor.rooms.find((room) => room.id === localRoom.id);
configuredRoom.settings = { temp_source: 'entity:sensor.override_temperature' };
card._serverCfg = { ...config, spaces: [...config.spaces] };
card._cfgEpoch++;
card.hass = {
...baseHass,
states: {
...baseHass.states,
'sensor.override_temperature': {
entity_id: 'sensor.override_temperature', state: '17',
attributes: { device_class: 'temperature', unit_of_measurement: '°C' },
},
},
};
card.requestUpdate();
await card.updateComplete;
result.explicitSourceWins = card._roomTemp(card._spaceModel().rooms
.find((room) => room.id === localRoom.id)) === 17;
return result;
});
checkAll(out);
await finish(browser, out);