Skip to content

Floorplans

A floorplan is an image — PNG, SVG, or PDF — bound to a sublocation (area). The SDK exposes full CRUD plus an download_image helper for fetching the stored bytes back.

Wire shape (probed from the live server)

The server's createFloorplan schema requires:

  • sublocation_uid (string)
  • image: {original_filename: string, file: <base64>} (object, both keys required)

Optional: display_name, image_scale, image_rotation, image_offset_x, image_offset_y.

There is no name field on floorplans — use display_name for the user-facing label.

Imageless floorplans are not permitted — the server rejects creates without an image with HTTP 422.

Create — from in-memory bytes

with open("warehouse.png", "rb") as fh:
    plan = client.floorplans.create(
        sublocation_uid=area.uid,
        image_bytes=fh.read(),
        original_filename="warehouse.png",
    )
print(plan.uid, plan.url)

Create — from a path

The SDK reads the file and derives original_filename from the path's basename:

plan = client.floorplans.create(
    sublocation_uid=area.uid,
    image_path="warehouse.png",
    display_name="Warehouse 12 — Bay 3",
)

image_bytes/original_filename and image_path are mutually exclusive — pass one or the other, not both. Neither raises ValueError before any HTTP call (it would be rejected anyway).

Update — image optional

floorplans.update(uid, …) only sends the fields you pass. To keep the stored image and change only metadata:

client.floorplans.update(plan.uid, display_name="Warehouse 12 — Bay 4")

To replace the image, pass new image input:

client.floorplans.update(
    plan.uid,
    image_bytes=new_bytes,
    original_filename="warehouse-v2.png",
)

Delete

client.floorplans.delete(plan.uid)

The server removes both the metadata row and the stored image file. A subsequent GET of the URL returns 404.

Download the image

download_image accepts either a Floorplan instance (uses its .url) or a uid (calls get(uid) first to resolve):

data = client.floorplans.download_image(plan)
with open("downloaded.png", "wb") as fh:
    fh.write(data)

For very large files (production floorplans can be tens of MB of PDF), stream directly to disk:

with open("warehouse.pdf", "wb") as fh:
    client.floorplans.download_image(plan, stream_to=fh)

stream_to= returns None; the file is written incrementally with a 64 KB chunk size. The read timeout is automatically extended to 180 seconds for the image GET — matches the desktop site-plan client's Network::Timeout::Resource.

Log redaction note

Image payloads (base64-encoded) can be tens of KB to MB. The SDK's redaction filter automatically truncates any logged string longer than 1024 chars to <truncated N bytes>, keeping DEBUG logs readable when you ask for them. Your actual image data is never logged in plain text. See Security for the full redaction guarantee.

Server-side re-encoding

The live server re-encodes PNG uploads on storage (likely to normalize / compress). Downloaded bytes are NOT byte-identical to uploaded bytes, but the dimensions and image content are preserved. Tests that need to verify "the image was replaced" should distinguish by width/height, not by byte equality.