Skip to content

Add possibility to per-map override a Custom Floor Plan - #42

Open
java-devil wants to merge 1 commit into
Python-roborock:mainfrom
java-devil:feature/custom-map-background
Open

Add possibility to per-map override a Custom Floor Plan#42
java-devil wants to merge 1 commit into
Python-roborock:mainfrom
java-devil:feature/custom-map-background

Conversation

@java-devil

Copy link
Copy Markdown

Hey follow-up to: #16

There are two alternative solutions to the same problem currently "hanging".

However, based on my personal experience I believe a more user-friendly approach is required. Especially one that is resilient to the map inevitably drifting with time. One which allows the User to manually self-correct at any moment. Preferably from the UI. While supporting multiple floors.

Given HAs limitations - this is the most comprehensive flow that I was able to come up with (safe for something crazy to maintain like a full-blown JS custom card):

1. `Settings` → `Devices & Services` → `Roborock Custom Map` → press `Configure`.
2. Pick a Floor (only if > 1 available).
3. Choose your Floor Plan (PNG, JPEG or WebP); a Live Preview appears as you do so.
4. Press `Submit` — the Floor Plan applies immediately as-is, with no adjustments.
5. On the next view, optionally adjust the horizontal/vertical offset/scale
   of the Custom Floor Plan, or the relative rotation of the Physical Walls.
   The Physical Walls are imposed over the Custom Floor Plan for your convenience.
   The Live Preview refreshes in near-real-time.
6. Press `Submit` once more — to keep the adjustments,
   or simply press `X` — to keep the Floor Plan as-is.

Upload Screen

image

Adjustment Screen

image

Removal Screen

image

Full Disclosure

I am no "Pythonista". Nor am I very aware of the idioms of HAs development. I leaned on an LLM quite a bit during development, and it did end up replacing in the end quite a bit of my initial draft. Having said that. I AM a professional SE. I did know what to prompt, what to check, and did so meticulously. This entire effort took a few solid days over many iterations / bugfixes.

Cheers,
Adam

@Lash-L
Lash-L self-requested a review July 23, 2026 13:53
@java-devil

Copy link
Copy Markdown
Author

@Lash-L some weird shenanigans seem to be happening on your repositories. This PR seems to have somehow fallen in the cracks, and it doesn't display on the open PR list. There is in fact only one PR there, despite the number next to PRs still claming otheriwse.

Please confirm that you still have visibility into this issue, and that I don't have to reopen it. There is no urgency to you reviewing the code. I just need a quick "I see you, I'll get to this eventually" ;)

@Lash-L

Lash-L commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Haha I knew this PR existed! Was confused when I didn't see it.

My account was false flagged banned. I got my account back but there's some weirdness. I see it!

@java-devil

java-devil commented Jul 29, 2026

Copy link
Copy Markdown
Author

No worries.

Lemme know when you will want to discuss anything.

At your pace.

Cheers :)

@Lash-L Lash-L left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments stick out to me!

rot_w, rot_h, offset_x, offset_y, ovr_w, ovr_h
)

canvas = Image.new("RGBA", (canvas_w, canvas_h), _TRANSPARENT)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can blow up memory 16,384 × 16,384 is about 1GB. Add a total pixel limit too keep things safe

)

canvas = Image.new("RGBA", (canvas_w, canvas_h), _TRANSPARENT)
canvas.paste(bg, (round(offset_x - origin_x), round(offset_y - origin_y)), bg)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use canvas.alpha_composite(bg, dest=(...)), so that semi transparent floor plans/ webP will work

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds per-map Custom Floor Plan overrides to the Roborock Custom Map integration, enabling users to upload a floor plan image per map and interactively tune its placement (offset/scale) with a live preview, while keeping calibration aligned for the Xiaomi map card.

Changes:

  • Introduces an options flow UI for per-map upload/adjust/remove of a Custom Floor Plan, including a live preview via websocket.
  • Adds a background-override rendering pipeline that composites the custom floor plan under Roborock drawables while preserving calibration alignment across map crop drift.
  • Refactors rotation storage/signaling to support being updated from the options flow as well as the select entity.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Updates documentation to describe the new Custom Floor Plan workflow and bumps the minimum HA version note.
custom_components/roborock_custom_map/translations/en.json Adds English UI strings for the new options flow steps and error/abort messages.
custom_components/roborock_custom_map/translations/de.json Adds German UI strings for the new options flow steps and error/abort messages.
custom_components/roborock_custom_map/select.py Updates rotation handling to use shared key helpers and to accept rotation changes triggered by the options flow.
custom_components/roborock_custom_map/preview.py Adds preview session storage and a websocket command to support near-real-time flow previews.
custom_components/roborock_custom_map/map_render.py Adds synchronous (executor) rendering utilities for compositing the custom floor plan with map overlays/masks and validating uploads.
custom_components/roborock_custom_map/manifest.json Adds dependencies needed for file upload + websocket preview and bumps integration version.
custom_components/roborock_custom_map/image.py Extends map image entity to render the composite (preview/saved) and adjust calibration points accordingly, with caching.
custom_components/roborock_custom_map/const.py Adds new constants and shared helpers (keys, signals, runtime rotation storage, override scale parsing).
custom_components/roborock_custom_map/config_flow.py Implements the options flow steps (select map, upload, tune, remove) and persists override placement anchored in vacuum coordinates.
custom_components/roborock_custom_map/init.py Adds update listener-based refresh for changed overrides, preview session storage initialization, and cleanup of stored override files.
.gitignore Adds __pycache__/ to gitignore.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +431 to +434
rotation = self._get_rotation(preview)
topleft = self._override_topleft(map_content, rotation, preview)
if topleft is not None and self._override_render_ok is not False:
transform = map_render.MapTransform.from_dimensions(
Comment on lines +144 to +145
bg = Image.open(io.BytesIO(bg_bytes))
bg = ImageOps.exif_transpose(bg).convert("RGBA")
Comment on lines +572 to +583
process_uploaded_file consumes the temp file, so re-submitting the same
(already consumed) id raises ValueError; treat that as an invalid image
so the user is simply asked to pick the file again. A failed read of
the temp file surfaces as a form error instead of crashing the flow.
"""
try:
with process_uploaded_file(self.hass, file_id) as path:
data = path.read_bytes()
except ValueError as err:
raise map_render.InvalidBackgroundImage("invalid_image") from err
except OSError as err:
raise map_render.InvalidBackgroundImage("upload_failed") from err
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.

3 participants