Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ Sources: Valetudo, python-miio, hass-xiaomi-miot, r/Xiaomi, r/homeassistant
- OTA firmware management

## Completed
- ✅ Mission Control vacuum-extend value decoding — the advanced diagnostics panel now translates the known `siid 4` enum-backed values into human-readable labels while still showing the raw number for debugging (for example `Quiet · raw 0`, `Attached · raw 1`, `Enabled · raw 1`). Added a TDD dashboard regression that failed before the new formatter/mappings were introduced. Research notes: the official `xiaomi.vacuum.c102gl` MIoT/OpenHAB mappings still identify `cleaning-mode` as `0=Quiet/1=Standard/2=Medium/3=Strong`, `mop-mode` as `1=Low/2=Medium/3=High`, `waterbox-status` as `0=No/1=Yes`, and both `break-point-restart` plus `carpet-press` as `0=Close/1=Open`; `openhab/openhab-addons` mirrors the same enums for the X20+ database; and `hass-xiaomi-miot` issue `#2767` still keeps hardware-dependent room-cleaning changes in the higher-risk bucket, which made another diagnostics-only UX ship the safer unattended choice. (2026-05-26)
- 🔜 Next planned item: Mission Control vacuum-extend diagnostics expansion — add the next confirmed `siid 4` controls (`child-lock`, `smart-wash`, and `carpet-avoidance`) to the advanced diagnostics panel with the same human-readable-plus-raw formatting so dashboard users can inspect the full known writable settings set at a glance. (Planned 2026-05-26)
- ✅ Mission Control vacuum-extend diagnostics copy cleanup — the advanced diagnostics panel now relabels the known `siid 4` fields as `Cleaning Mode`, `Mop Mode`, `Waterbox Status`, `Task Status`, `Resume After Charge`, and `Carpet Boost` instead of showing generic `Unknown` placeholders, while still leaving the underlying raw values untouched for debugging. Added a TDD dashboard regression that failed on the old labels before updating the frontend copy. Research notes: the official `xiaomi.vacuum.c102gl` MIoT spec still exposes these `vacuum-extend` properties under `siid 4` (`mop-mode`, `waterbox-status`, `task-status`, `break-point-restart`, `carpet-press`); `openhab/openhab-addons` mirrors the same channel names for the X20+ model; and `hass-xiaomi-miot` issue `#2767` still shows room-cleaning behavior is the riskier area, which kept this run focused on a safe diagnostics UX win instead of hardware-dependent action changes. (2026-05-14)
- 🔜 Next planned item: Mission Control vacuum-extend value decoding — translate the known raw `siid 4` enum values in the advanced diagnostics panel (for example `mop-mode`, `waterbox-status`, `break-point-restart`, and `carpet-press`) into human-readable labels while preserving the raw values for debugging. (Planned 2026-05-14)
- ✅ Waterbox attachment status surfaced across CLI + Mission Control — `status()` now reads the official X20+ `vacuum-extend` `waterbox-status` property (`siid 4 piid 6`) and exposes it as `attached`/`detached`, while the rich CLI formatters and Mission Control status tags now show whether the mop water box is present without forcing users into raw property dumps. Added TDD regression coverage for the cloud status decoder, full-status formatter, and dashboard HTML hook before implementation. Research notes: the local backlog/spec mapping already identified `siid 4 piid 6` as `waterbox-status`; the official `xiaomi.vacuum.c102gl` MIoT/OpenHAB database maps it as a read-only `0 = No`, `1 = Yes` enum; and that made this a safe, user-visible diagnostics improvement for mopping/setup issues without depending on on-device room-clean verification. (2026-05-12)
- ✅ State 21 naming fix (`WashingMopPause`) — `status()`, `full_status()`, Mission Control API responses, and dashboard state maps now keep the official X20+ state-21 name instead of mislabeling it as `Water Tank Alert`, while still surfacing separate recovery guidance to check/refill the base tanks when that pause appears. Also updated the tank auto-reset heuristic to key off the real state-21 name so the existing estimate-reset behavior keeps working after the label correction. Added TDD regression coverage for the cloud status decoder, `full_status()` alert wiring, Mission Control `/api/status`, tank-update auto-reset logic, and the dashboard HTML state map before implementation. Research notes: the local X20+ MIoT mapping/tests already called state 21 `WashingMopPause`, and the adjacent `openhab/openhab-addons` MIIO binding docs list the same status enum label for `xiaomi.vacuum.c102gl`; that made this a safe terminology/UX fix while preserving the existing tank-service recovery advice. (2026-05-11)
- ✅ Estimated cleaning energy from clean logs — `clean_history()` now derives a clearly labeled lifetime energy estimate from the X20+ clean-log totals, and Mission Control plus the rich CLI history surfaces now show that estimate as `kWh @ 75W` so users do not have to do the math manually. Added TDD regression coverage for the cloud-history payload, full-status formatter, report formatter, and dashboard history card before implementation. Research notes: Xiaomi's published `Xiaomi Robot Vacuum X20+` specs list `Rated Power 75W`, the official `xiaomi.vacuum.c102gl` clean-log fields still expose only aggregate cleaning time/count/area, and adjacent Home Assistant energy tooling (`Vortitron/energy-sensor-generator`) reinforced keeping the output explicitly in kWh while labeling it as an estimate rather than measured telemetry. (2026-05-08)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.2](https://github.com/dacrypt/xiao/compare/v0.7.1...v0.7.2) (2026-05-26)

### Fixed

* **dashboard:** decode the known X20+ `vacuum-extend` diagnostics enums into human labels while preserving each raw `siid 4` value for debugging

## [0.7.1](https://github.com/dacrypt/xiao/compare/v0.7.0...v0.7.1) (2026-05-14)

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "xiao-cli"
version = "0.7.1"
version = "0.7.2"
description = "CLI tool and web dashboard to control Xiaomi/Roborock robot vacuums via cloud API"
readme = "README.md"
license = { file = "LICENSE" }
Expand Down
2 changes: 1 addition & 1 deletion src/xiao/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""xiao - CLI tool to control Xiaomi/Roborock robot vacuums."""

__version__ = "0.6.5"
__version__ = "0.7.2"
24 changes: 19 additions & 5 deletions src/xiao/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2900,6 +2900,20 @@ <h3 id="confirmMsg">Start cleaning?</h3>
</div>
`;

const vacuumExtendCleaningModeMap = { 0: 'Quiet', 1: 'Standard', 2: 'Medium', 3: 'Strong' };
const vacuumExtendMopModeMap = { 1: 'Low', 2: 'Medium', 3: 'High' };
const vacuumExtendWaterboxStatusMap = { 0: 'Detached', 1: 'Attached' };
const vacuumExtendToggleMap = { 0: 'Disabled', 1: 'Enabled' };

function formatVacuumExtendEnum(value, labels) {
if (value === null || value === undefined || value === '') return '--';
const numericValue = Number(value);
const hasNumericLabel = Number.isFinite(numericValue) && Object.hasOwn(labels, numericValue);
const label = hasNumericLabel ? labels[numericValue] : labels[value];
if (label === undefined) return `${value}`;
return `${label} · raw ${value}`;
}

// Clean Log extended (siid 4)
const cleanLogArea = document.getElementById('cleanLogArea');
const cl = d.clean_log || {};
Expand Down Expand Up @@ -2946,27 +2960,27 @@ <h3 id="confirmMsg">Start cleaning?</h3>
<div class="cleanlog-label">Total Area</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop4 ?? '--'}</div>
<div class="cleanlog-val" style="color:var(--purple)">${formatVacuumExtendEnum(cl.prop4, vacuumExtendCleaningModeMap)}</div>
<div class="cleanlog-label">4.4 Cleaning Mode</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop5 ?? '--'}</div>
<div class="cleanlog-val" style="color:var(--purple)">${formatVacuumExtendEnum(cl.prop5, vacuumExtendMopModeMap)}</div>
<div class="cleanlog-label">4.5 Mop Mode</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop6 ?? '--'}</div>
<div class="cleanlog-val" style="color:var(--purple)">${formatVacuumExtendEnum(cl.prop6, vacuumExtendWaterboxStatusMap)}</div>
<div class="cleanlog-label">4.6 Waterbox Status</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop7 ?? '--'}</div>
<div class="cleanlog-label">4.7 Task Status</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop11 ?? '--'}</div>
<div class="cleanlog-val" style="color:var(--purple)">${formatVacuumExtendEnum(cl.prop11, vacuumExtendToggleMap)}</div>
<div class="cleanlog-label">4.11 Resume After Charge</div>
</div>
<div class="cleanlog-item" style="border-color: rgba(170,102,255,0.2)">
<div class="cleanlog-val" style="color:var(--purple)">${cl.prop12 ?? '--'}</div>
<div class="cleanlog-val" style="color:var(--purple)">${formatVacuumExtendEnum(cl.prop12, vacuumExtendToggleMap)}</div>
<div class="cleanlog-label">4.12 Carpet Boost</div>
</div>
</div>
Expand Down
14 changes: 14 additions & 0 deletions tests/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,17 @@ def test_dashboard_relabels_known_vacuum_extend_fields_in_clean_log_panel(self):
assert "4.7 Unknown" not in html
assert "4.11 Unknown" not in html
assert "4.12 Unknown" not in html

def test_dashboard_decodes_known_vacuum_extend_enum_values_while_preserving_raw_values(self):
html = _dashboard_html()

assert "const vacuumExtendCleaningModeMap = { 0: 'Quiet', 1: 'Standard', 2: 'Medium', 3: 'Strong' };" in html
assert "const vacuumExtendMopModeMap = { 1: 'Low', 2: 'Medium', 3: 'High' };" in html
assert "const vacuumExtendWaterboxStatusMap = { 0: 'Detached', 1: 'Attached' };" in html
assert "const vacuumExtendToggleMap = { 0: 'Disabled', 1: 'Enabled' };" in html
assert "return `${label} · raw ${value}`;" in html
assert "formatVacuumExtendEnum(cl.prop4, vacuumExtendCleaningModeMap)" in html
assert "formatVacuumExtendEnum(cl.prop5, vacuumExtendMopModeMap)" in html
assert "formatVacuumExtendEnum(cl.prop6, vacuumExtendWaterboxStatusMap)" in html
assert "formatVacuumExtendEnum(cl.prop11, vacuumExtendToggleMap)" in html
assert "formatVacuumExtendEnum(cl.prop12, vacuumExtendToggleMap)" in html
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading