mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 16:38:31 +00:00
Vacuum adapter vs a live Dreame X50 Master
Checked against the real robot at the dacha: room centres arrive as plain x/y next to the bbox, and the active-map name (selected_map, 'Первый этаж'/'Второй этаж') lives on the VACUUM entity, not on the camera — without reading it both floors would silently share one calibration matrix. Parser and card resolver adjusted; the captured attribute shape is now a unit fixture.
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+15
-3
@@ -4420,6 +4420,18 @@ class HouseplanCard extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The active-map id. The camera rarely names its map; Dreame keeps the
|
||||
* human-readable one on the vacuum entity (selected_map, verified against a
|
||||
* live X50 Master) — without this both floors would share one matrix.
|
||||
*/
|
||||
private _vacMapId(d: DevItem, tele: { mapId: string }): string {
|
||||
if (tele.mapId !== 'default') return tele.mapId;
|
||||
const ve = this._vacEntity(d);
|
||||
const sel = ve ? this.hass?.states[ve]?.attributes?.selected_map : null;
|
||||
return sel ? String(sel) : 'default';
|
||||
}
|
||||
|
||||
/** Persist a solved matrix into marker.vacuum.calibration[mapId]. */
|
||||
private _vacSaveMatrix(markerId: string, source: string, mapId: string, matrix: Affine): void {
|
||||
const cfg = this._serverCfg;
|
||||
@@ -4458,7 +4470,7 @@ class HouseplanCard extends LitElement {
|
||||
if (res.residual > NORM_W * 0.05) {
|
||||
this._showToast(subst(this._t('vac.autocal_res_warn'), { rooms: String(res.matched.length) }));
|
||||
}
|
||||
this._vacSaveMatrix(d.id, src, tele.mapId, res.matrix);
|
||||
this._vacSaveMatrix(d.id, src, this._vacMapId(d, tele), res.matrix);
|
||||
this._showToast(subst(this._t('vac.autocal_done'), { rooms: String(res.matched.length) }));
|
||||
}
|
||||
|
||||
@@ -4471,7 +4483,7 @@ class HouseplanCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
this._markerDialog = null;
|
||||
this._vacCal = { markerId: d.id, source: src, mapId: tele.mapId, pairs: [] };
|
||||
this._vacCal = { markerId: d.id, source: src, mapId: this._vacMapId(d, tele), pairs: [] };
|
||||
}
|
||||
|
||||
private _vacCalClick(ev: MouseEvent): void {
|
||||
@@ -4516,7 +4528,7 @@ class HouseplanCard extends LitElement {
|
||||
if (!src) continue;
|
||||
const tele = readVacTelemetry(this.hass?.states[src]?.attributes);
|
||||
if (!tele) continue;
|
||||
const matrix = d.marker?.vacuum?.calibration?.[tele.mapId] as Affine | undefined;
|
||||
const matrix = d.marker?.vacuum?.calibration?.[this._vacMapId(d, tele)] as Affine | undefined;
|
||||
if (!matrix || matrix.length !== 6) continue;
|
||||
const rt = this._vacRt.get(d.id);
|
||||
const moving = rt?.moving ?? false;
|
||||
|
||||
@@ -120,6 +120,9 @@ export function readVacTelemetry(attrs: Record<string, any> | null | undefined):
|
||||
cx = (x0 + x1) / 2; cy = (y0 + y1) / 2;
|
||||
}
|
||||
}
|
||||
// Tasshack dreame-vacuum: the room centre is plain x/y (verified against
|
||||
// a live X50 Master; x/y sits within its own x0..x1 bbox)
|
||||
if (cx == null || cy == null) { cx = num(r.x); cy = num(r.y); }
|
||||
if (name && cx != null && cy != null) rooms.push({ id, name, cx, cy });
|
||||
}
|
||||
const mapId = String(attrs.map_name ?? attrs.current_map ?? attrs.map_index ?? attrs.selected_map ?? 'default');
|
||||
|
||||
@@ -48,6 +48,19 @@ test('readVacTelemetry: Valetudo/Tasshack shapes + junk safety', () => {
|
||||
assert.ok(!isVacSourceState({ attributes: { battery: 1 } }));
|
||||
});
|
||||
|
||||
test('readVacTelemetry: Tasshack room centres come as plain x/y', () => {
|
||||
// shape captured from a live Dreame X50 Master (dacha, 2026-07-31)
|
||||
const t = readVacTelemetry({
|
||||
vacuum_position: { x: 1399, y: -55, a: 181 },
|
||||
rooms: { 2: { room_id: 2, name: 'Кладовка', x0: 800, y0: -2000, x1: 4200, y1: 300, x: 2575, y: -825 } },
|
||||
});
|
||||
assert.equal(t.rooms[0].name, 'Кладовка');
|
||||
// bbox wins when present (both are valid anchors); x/y covers bbox-less dialects
|
||||
assert.equal(t.rooms[0].cx, 2500);
|
||||
const t2 = readVacTelemetry({ vacuum_position: { x: 0, y: 0 }, rooms: { 2: { name: 'Кладовка', x: 2575, y: -825 } } });
|
||||
assert.equal(t2.rooms[0].cx, 2575);
|
||||
});
|
||||
|
||||
test('autoCalibrate matches by name and solves', () => {
|
||||
const f = ([x, y]) => [0.01 * x + 100, -0.01 * y + 900];
|
||||
const vac = [
|
||||
|
||||
Reference in New Issue
Block a user