mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
127 lines
5.8 KiB
JavaScript
127 lines
5.8 KiB
JavaScript
// #162: карты робота живут на разных этажах, док — на своём.
|
|
//
|
|
// Смок проверяет ровно то, чего не мог проверить ни один прежний: раньше
|
|
// маркер робота отбирался по пространству дока, поэтому на другом этаже
|
|
// оверлея не существовало в принципе.
|
|
import { launch, checkAll, finish } from './serve.mjs';
|
|
const { page, browser } = await launch();
|
|
|
|
const out = await page.evaluate(async () => {
|
|
const c = window.__card;
|
|
const o = {};
|
|
const sr = () => c.shadowRoot || c.renderRoot;
|
|
const M1 = [0.5, 0, 0, 0, 0.5, 0];
|
|
const M2 = [0.25, 0, 0, 0, 0.25, 0];
|
|
const attrs = (mapName) => ({ vacuum_position: { x: 600, y: 800, a: 45 }, map_name: mapName });
|
|
|
|
c.hass = { ...c.hass,
|
|
entities: { ...c.hass.entities,
|
|
'vacuum.robo': { entity_id: 'vacuum.robo', platform: 'demo', disabled_by: null },
|
|
'camera.robo_map': { entity_id: 'camera.robo_map', platform: 'demo', disabled_by: null } },
|
|
states: { ...c.hass.states,
|
|
'vacuum.robo': { state: 'cleaning', attributes: { friendly_name: 'Робот' } },
|
|
'camera.robo_map': { state: 'idle', attributes: attrs('m1') },
|
|
} };
|
|
const cfg = c._serverCfg;
|
|
cfg.markers = cfg.markers || [];
|
|
cfg.markers.push({
|
|
id: 'e_vacuum_robo', binding: 'entity:vacuum.robo', space: 'f1',
|
|
vacuum: {
|
|
source: 'camera.robo_map',
|
|
map_routes: [
|
|
{ id: 'vr1', source: 'camera.robo_map', map_id: 'm1', space: 'f1', calibration: M1 },
|
|
{ id: 'vr2', source: 'camera.robo_map', map_id: 'm2', space: 'garden', calibration: M2 },
|
|
],
|
|
},
|
|
});
|
|
c._layout['e_vacuum_robo'] = { s: 'f1', x: 0.1, y: 0.1 };
|
|
c._regSignature = '';
|
|
c._setMode('view');
|
|
c._space = 'f1';
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
|
|
const puck = () => sr().querySelector('.vacpuck');
|
|
const dock = () => sr().querySelector('.dev[data-id="e_vacuum_robo"]');
|
|
const warn = () => sr().querySelector('.vacwarn');
|
|
|
|
// Карта m1 → первый этаж: робот и док на одном этаже, как и раньше.
|
|
o.floor1DockThere = !!dock();
|
|
o.floor1PuckThere = !!puck();
|
|
o.floor1NoWarning = !warn();
|
|
|
|
// Робот переехал на карту m2 → второй этаж.
|
|
c.hass = { ...c.hass, states: { ...c.hass.states,
|
|
'camera.robo_map': { state: 'idle', attributes: attrs('m2') } } };
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.floor1DockStays = !!dock();
|
|
o.floor1PuckGone = !puck();
|
|
|
|
c._space = 'garden';
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.floor2PuckThere = !!puck();
|
|
o.floor2NoDock = !dock();
|
|
|
|
// Возврат на m1 возвращает картину без единой правки конфигурации.
|
|
const before = JSON.stringify(c._serverCfg.markers.find((m) => m.id === 'e_vacuum_robo').vacuum);
|
|
c.hass = { ...c.hass, states: { ...c.hass.states,
|
|
'camera.robo_map': { state: 'idle', attributes: attrs('m1') } } };
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.floor2PuckGoneAfterReturn = !puck();
|
|
c._space = 'f1';
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.floor1PuckBack = !!puck();
|
|
o.routesUntouched = JSON.stringify(
|
|
c._serverCfg.markers.find((m) => m.id === 'e_vacuum_robo').vacuum) === before;
|
|
|
|
// Несопоставленная карта: нигде не рисуем, но у дока говорим почему.
|
|
c.hass = { ...c.hass, states: { ...c.hass.states,
|
|
'camera.robo_map': { state: 'idle', attributes: attrs('m9') } } };
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.unmappedNoPuck = !puck();
|
|
o.unmappedWarns = !!warn();
|
|
o.unmappedWarnLabelled = !!warn()?.getAttribute('aria-label');
|
|
c._space = 'garden';
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.unmappedNoGhostOnOtherFloor = !puck();
|
|
|
|
// Донастройка матрицы с высоким residual обязана открыться на этаже
|
|
// МАРШРУТА: она решалась против его геометрии, а не геометрии дока.
|
|
c._space = 'f1';
|
|
c._setMode('devices');
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 120));
|
|
c._vacCalConfirm = {
|
|
markerId: 'e_vacuum_robo', source: 'camera.robo_map', mapId: 'm2',
|
|
routeId: 'vr2', space: 'garden', matrix: M2, rooms: 3, error: '80 см',
|
|
};
|
|
await c.updateComplete;
|
|
await c._vacApplyCalibrationProposal(true);
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 80));
|
|
o.manualFitSwitchedToRouteSpace = c._space === 'garden';
|
|
o.manualFitKeepsRoute = c._vacFit?.routeId === 'vr2';
|
|
c._vacFit = null;
|
|
c._setMode('view');
|
|
c._space = 'f1';
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 80));
|
|
|
|
// Робот встал — предупреждение снимается: тревожить нечем.
|
|
c._space = 'f1';
|
|
c.hass = { ...c.hass, states: { ...c.hass.states,
|
|
'vacuum.robo': { state: 'docked', attributes: { friendly_name: 'Робот' } } } };
|
|
await c.updateComplete; await new Promise((r) => setTimeout(r, 60));
|
|
o.dockedNoWarning = !warn();
|
|
|
|
return o;
|
|
});
|
|
|
|
checkAll(out, {
|
|
floor1DockThere: true, floor1PuckThere: true, floor1NoWarning: true,
|
|
floor1DockStays: true, floor1PuckGone: true,
|
|
floor2PuckThere: true, floor2NoDock: true,
|
|
floor2PuckGoneAfterReturn: true, floor1PuckBack: true, routesUntouched: true,
|
|
unmappedNoPuck: true, unmappedWarns: true, unmappedWarnLabelled: true,
|
|
unmappedNoGhostOnOtherFloor: true,
|
|
manualFitSwitchedToRouteSpace: true, manualFitKeepsRoute: true,
|
|
dockedNoWarning: true,
|
|
});
|
|
await finish(browser);
|