Skip to content

feat(clean): expose clean settings as HA entities (work mode / fan / water / mop strength / passes)#50

Open
jgus wants to merge 3 commits into
sjmotew:masterfrom
jgus:feat/clean-settings
Open

feat(clean): expose clean settings as HA entities (work mode / fan / water / mop strength / passes)#50
jgus wants to merge 3 commits into
sjmotew:masterfrom
jgus:feat/clean-settings

Conversation

@jgus

@jgus jgus commented Jun 14, 2026

Copy link
Copy Markdown

Builds on #49 (fix(clean): room cleaning via clean/start_clean with a parameterized CleanParam) and is intended to be merged after it — it uses that PR's WorkMode / parameterized start_rooms. While #49 is open, this PR's diff includes its commit as well; merge #49 first and this reduces to just the HA layer below, or take both together if you prefer.

Summary

Exposes the room-clean parameters decoded in #49 as Home Assistant controls, so users can set the work mode, suction, water, mop strength, and pass count and have them applied to room cleans.

What it adds

  • select entities (config category):
    • Clean mode — Vacuum / Mop / Vacuum then mop / Vacuum and mop
    • Mopping humidity — Slightly dry / Normal / Slightly wet
    • Mop strength — Normal / High
  • number entity: Cleaning passes (1–3)
  • The vacuum's existing fan_speed is threaded through the same settings.

All are backed by a single CleanSettings dataclass on the coordinator (the source the clean-start path reads). async_clean_segments threads them into start_rooms; water and fan also apply live while the robot is cleaning.

Persistence

Values persist across restarts via HA's RestoreEntity (RestoreSelect / RestoreNumber / RestoreEntity) — set once and kept. No manual storage; restore_state replays the last value into CleanSettings on startup.

Labels

Option labels use the app's user-visible wording (same convention as #49's fan labels). Two caveats:

  • Mop strength — the app exposes no user-visible labels for it, so "Normal/High" are best-effort (it's a real CleanParam field the app otherwise auto-manages).
  • French select labels are best-effort, not verified against the app's fr-FR.

Testing

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds Home Assistant select and number entities that expose the room-clean parameters decoded in #49 — work mode, mopping humidity, mop strength, and pass count — plus threads the existing fan speed through the same CleanSettings dataclass on the coordinator. All values persist across restarts via RestoreEntity/RestoreNumber and are applied to both whole-house and room-specific cleans.

  • New entities (all config category): three SelectEntity instances (clean mode, mopping humidity, mop strength) and one RestoreNumber (cleaning passes 1–3), all sharing a single CleanSettings dataclass on the coordinator that start_rooms reads at clean-start time.
  • Fan speed overhaul: FanLevel enum values are updated to match the actual APK proto integers (MUTE=1, NORMAL=2, STRONG=3, DEEP=4, SUPER=5); old lowercase aliases (quiet, normal, max) are kept in FAN_SPEED_MAP for backward-compat service calls while the reported fan_speed and the dropdown list switch to sentence-case canonical labels.
  • Whole-house start rerouted: async_start now enumerates all rooms via clean/start_clean (matching the app's allRoomIds() path) instead of clean/plan/start, applying CleanSettings; a best-effort fallback to client.start() fires only when no map rooms are available.

Confidence Score: 5/5

Safe to merge; the new entity layer is well-isolated, restore logic is straightforward, and the protobuf payload changes are covered by real-device validation and 167 passing tests.

The change introduces a clean shared-state design (CleanSettings dataclass) that three independent entity types write to and the clean-start path reads from. The FanLevel enum realignment is intentional and correctly handled through both the backward-compat alias map and the _FAN_LABELS reverse map. The whole-house start rerouting through start_rooms is architecturally sound.

No files require special attention.

Important Files Changed

Filename Overview
custom_components/narwal/select.py New file adding three config SelectEntity instances backed by coordinator.clean_settings; uses RestoreEntity for persistence, live setter for mop humidity while cleaning.
custom_components/narwal/number.py New file adding a RestoreNumber for cleaning pass count (1–3); reads/writes coordinator.clean_settings.passes.
custom_components/narwal/vacuum.py async_start rerouted to enumerate all map rooms via start_rooms; fan speed persistence moved to coordinator.clean_settings via RestoreEntity.
narwal_client/client.py _build_start_clean_payload replaces _build_room_clean_payload with full CleanParam support; start_rooms uses clean/start_clean with NOT_READY retry.
narwal_client/const.py FanLevel enum updated to APK proto integers; adds MopStrengthLevel, WorkMode enums; NOT_READY added.

Reviews (3): Last reviewed commit: "fix(clean): whole-house start cleans all..." | Re-trigger Greptile

Comment thread custom_components/narwal/const.py Outdated
Comment thread narwal_client/client.py
Comment thread custom_components/narwal/select.py
jgus and others added 2 commits June 14, 2026 18:09
…CleanParam (sjmotew#25, sjmotew#37)

Room cleans were sent to clean/plan/start, but on Flow firmware that is
StartWithPlan{planId, mapId} — it starts a saved plan by id and ignores any room
payload, so the robot undocked and wandered instead of cleaning the selection.

Switch start_rooms() to clean/start_clean (StartClean → CleanTask). Track the
active map id (MapData.map_id, get_map field 2.1), which the CleanTask requires.
clean/start_clean only works docked; from STANDBY the robot returns a new code 4
(CommandResult.NOT_READY) — retry briefly while docked.

Build the CleanParam from named parameters: start_rooms() takes
work_mode/fan/water/mop_strength/passes with defaults at the call site
(vacuum-and-mop, standard suction/water/mop, single pass). Names and enums match the
app's CleanTask proto: WorkMode (= robot_work_mode_*, whose value is CleanTask.taskType),
corrected FanLevel/MopHumidity, added MopStrengthLevel; fan_speed labels use the app's
user-visible suction names (quiet/standard/strong/super powerful/ultra powerful).
Pre-rename fan_speed values (normal/max) remain accepted for back-compat.

Validated live on a Flow 2: room clean returns SUCCESS and the robot cleans the
selected rooms (confirmed via clean/current_clean_task/get). Both client copies synced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…water / mop strength / passes)

Adds Home Assistant controls for the room-clean parameters, backed by a CleanSettings
dataclass on the coordinator (the single source the clean-start path reads):

- select entities: work mode (Vacuum / Mop / Vacuum then mop / Vacuum and mop),
  mopping humidity (Slightly dry / Normal / Slightly wet), mop strength (Normal / High);
- number entity: cleaning passes (1-3);
- the vacuum's fan_speed is threaded through the same settings.

Entity labels use the app's user-visible wording. Values persist across restarts via
RestoreEntity (RestoreSelect / RestoreNumber / RestoreEntity) — set once and kept.
async_clean_segments threads them into start_rooms; water and fan also apply live while
cleaning.

Built on sjmotew#49 (parameterized start_rooms / WorkMode).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jgus jgus force-pushed the feat/clean-settings branch from cc8b075 to 225274e Compare June 15, 2026 00:13
async_start sent clean/plan/start (StartWithPlan), which replays the saved current plan — the last room selection — so a whole-house Start re-ran the previous room-subset clean instead of cleaning the house. Enumerate every cleanable room and clean via clean/start_clean (start_rooms), matching the app's allRoomIds() path; fall back to the saved-plan start only when no map rooms are known.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant