Skip to content

Reports

Three flows: heatmap (poll until ready), PWS aggregation (materialize an event report), CSV downloads (zone-activity, alarms).

Heatmap — polling

from datetime import datetime, timezone

start = datetime(2024, 1, 1, tzinfo=timezone.utc)
end   = datetime(2024, 1, 2, tzinfo=timezone.utc)

heatmap = client.reports.heatmap(
    start=start,
    end=end,
    tag_uids=["t-1", "t-2"],
    poll_interval=2.0,
    timeout=300.0,
)
print(heatmap.image_url)

Behind the scenes: POST /obsolete/report (the live path, despite the URL — RESEARCH discrepancy #5), then GET-polling until output.url is populated. The SDK raises:

  • ReportFailed — server reported error in the poll body.
  • ReportTimeout — polling exceeded timeout. The exception carries report_uid so you can resume polling via client.reports.get(uid) later.
from rtls_sdk import ReportFailed, ReportTimeout

try:
    heatmap = client.reports.heatmap(start=start, end=end, tag_uids=["t-1"], timeout=10.0)
except ReportTimeout as exc:
    # Resume later with exc.report_uid
    ...
except ReportFailed:
    ...

timeout=None (default) is unbounded — useful for batch jobs that must wait however long the server takes.

PWS event aggregation

report = client.reports.pws(
    trackable_uid=["t-1", "t-2"],
    start=start,
    end=end,
    timezone="UTC",
)
print(len(report.events))

Materializes every page into one PwsReport. For unbounded windows, prefer the iterator surface (client.events.iter_pws(...)) — see pagination.

CSV downloads

Zone activity (chunked)

blob = client.reports.zone_activity_csv(
    start=start,
    end=end,
    area_uids=["a-1", "a-2"],
)
blob.write_to("zone-activity.csv")

The server returns the CSV in chunks via the content-next-page header. The SDK follows the chain, strips duplicate header rows, and returns one coherent CSV blob.

For very large windows, stream directly to disk:

with open("zone-activity.csv", "wb") as fh:
    client.reports.zone_activity_csv(
        start=start,
        end=end,
        area_uids=["a-1"],
        stream_to=fh,
    )

stream_to= returns None; the file is written incrementally.

Alarms

blob = client.reports.alarms_csv(
    start=start,
    end=end,
    site_uid="s-1",
    alarm_types=["fall", "battery_low"],
    include_banner=True,
)
blob.write_to("alarms.csv")

include_banner=True (default) prepends the multi-line `#Site / #From /

To` banner that the JS reference client emits, so downstream tools that

match on the banner work unchanged. Pass False for the bare server CSV.