Skip to content

Models

Typed return types for every resource. Pydantic v2 under the hood; treat each as a frozen-ish dataclass for read-only access.

Core entities

Tag

Bases: _BaseModel

A tracked object (trackable / tag / asset).

Field aliases tolerate the embedded forms the server uses inside other entities. Position and node sub-objects are eagerly parsed into typed models when present.

The trackable root carries no MAC of its own — the bound hardware's MAC lives on the embedded node sub-object. mac_address is a convenience that reads it from there (None when no node is attached).

The server has been observed to return groups: null on freshly- created trackables (an unbound tag has no groups, but the field is present). The SDK coerces None to [] so the typed list[str] surface stays clean for consumers.

mac_address property

mac_address

The bound node's MAC, or None when no node is attached.

Sourced from node.mac_address — the trackable root has no mac_address field of its own.

TagTemplate

Bases: _BaseModel

A reusable shape for creating new trackables.

Site

Bases: _BaseModel

A top-level physical location.

Sites contain :class:Area rows (sublocations). Fields beyond uid / name are optional and surface what the server returns on a successful create / read.

shift is typed as Any because the wire format varies by deployment (some servers emit {} / {...}, others emit [] / [...] depending on whether shifts are configured). The SDK doesn't take sides — read it as-is.

address / country / timezone are optional placement metadata the desktop site-plan client sends at create time and the server echoes back on read.

Area

Bases: _BaseModel

A sublocation inside a site.

Areas (called sublocations on the wire) are the geographic units that zones, positions, and trackables are scoped to. site_uid is the parent site; the server names that field location_uid, so the SDK exposes the canonical site_uid and maps it on read.

Floorplan

Bases: _BaseModel

A floorplan image attached to an area (sublocation).

area_uid is the parent area; the server names that field sublocation_uid, so the SDK exposes the canonical area_uid and maps it on read (mirroring :class:Area's site_uid).

The server returns display_name as the user-facing label and url as a relative path of the form /uploads/floorplan/image/<uid>/<original_filename>. There is no name field — the M9 read-only model carried one in error, fixed in M10.

Pixel dimensions (width, height) are server-computed from the uploaded image; size is the byte size.

Fields kept permissive (str | None, Any) because some deployments null-out values the SDK doesn't know what to do with yet (e.g. img_blob is None on the test server).

Zone

Bases: _BaseModel

A zone or buffer.

M1 ships the discriminator fields. M2 adds the geometric shape data (polygon points, circle radius) and zone-type metadata.

ZoneEvent

Bases: _BaseModel

A zone-traversal event (enter / exit / dwell).

Group

Bases: _BaseModel

A trackable group.

Groups are flat collections of trackables. Membership lives on the trackable side (Tag.groups); the group itself only carries a name and metadata.

User

Bases: _BaseModel

An RTLS user.

The wire-format fields isSuperAdmin / isSystemUser / isCompanyAdmin plus the raw role string are normalized into:

  • role: a single :class:Role enum value.
  • raw_flags: the four boolean flags untouched.

role property

role

Derived role for the user.

Resolution order (boolean flags win over raw string):

  1. is_super_admin → :attr:Role.SYSTEM_ADMIN (server-wide root).
  2. is_company_admin → :attr:Role.COMPANY_ADMIN.
  3. Raw role string, if it parses cleanly.
  4. Default → :attr:Role.PROJECT_USER.

The is_system_user flag is intentionally not mapped to a Role member — the server's role vocabulary has no system-user entry; the flag remains accessible via user.raw_flags.is_system_user for callers that need it.

UserFlags

Bases: _BaseModel

Raw boolean flags the server returns on every User object.

Node

Bases: _BaseModel

A hardware node (anchor/tag) registered to the system.

uid and mac_address are optional because the embedded node form inside Trackable carries fewer fields than the standalone-list form.

Anchor

Bases: _BaseModel

A WIN (wireless indoor navigation) anchor — fixed-position beacon.

Field names are canonical. The server's read payload nests placement under position; the before-validator flattens it so callers see x / y / z directly. area_uid is initialized from the wire sublocation_uid (top-level or under position, where it may also appear as sl_uid). The bound hardware stays under node — the anchor itself carries no MAC; read it from anchor.node.mac_address.

AnchorAssociation

Bases: _BaseModel

A binding between a hardware MAC address and a placed WIN anchor.

The server identifies the anchor side as win_uid on the wire; the SDK keeps that name on both the model and the method surface (e.g. :meth:AnchorAssociationsAPI.create) for round-trip clarity.

TagAssociation

Bases: _BaseModel

A binding between a hardware MAC address and a trackable.

TagAssociations are time-bounded: a trackable can wear different physical tags over its lifetime. closed_at is None on an open binding.

Position

Bases: _BaseModel

A position fix for a trackable.

Field names are canonical (the standalone-endpoint form). Wire-format aliases sl_uid / dev_id / mac are rewritten at parse time so consumers don't need to know which endpoint produced the data.

Reports & events

Report

Bases: _BaseModel

A generated report's metadata.

ReportType

Bases: str, Enum

Enumerated report types accepted by reports.list(..., type=).

Heatmap

Bases: _BaseModel

A completed heatmap report.

image_url is the URL of the rendered PNG/SVG (built from the server's hostname + /api/v2/heatmap/{uid} + any query the server returned).

HeatmapInput

Bases: _BaseModel

The request shape echoed back by the heatmap status endpoint.

PwsReport

Bases: _BaseModel

Aggregated PWS report — every page concatenated in memory.

For windows that span many pages, prefer :meth:EventsAPI.iter_pws (lazy) over :meth:ReportsAPI.pws (eager). The aggregate form is convenient for ad-hoc analysis; the lazy form is friendlier to RAM and lets you stop early.

PwsEvent

Bases: _BaseModel

One PWS event row from /external_events?event_type[]=pws.

Fields beyond uid/ts are deployment-specific; rely on .raw for anything the SDK doesn't expose explicitly.

CsvBlob dataclass

CsvBlob(content, filename=None)

An in-memory CSV download.

Attributes:

Name Type Description
content bytes

The full CSV bytes. Already-deduped of duplicate header rows when the report came back chunked.

filename str | None

Server-suggested filename from the response-filename header, or None if absent. Use it as the default when saving to disk.

write_to

write_to(path)

Save :attr:content to path.

Convenience helper — equivalent to Path(path).write_bytes(self.content).

Alarm

Bases: _BaseModel

An alarm event raised by the server.

Shape is consistent across the active / period / area / site endpoints — the format= query parameter switches between JSON (returns Alarm) and CSV (raw bytes, surfaced by report methods, not here).

Org

Project

Bases: _BaseModel

A project — a unit of access control + scoping.

Most operator-level resources (tags, zones, alarms, …) are scoped to a project. The currently-active project is set on the SDK via the project_uid= constructor argument or RTLS_PROJECT_UID env var.

Company

Bases: _BaseModel

A company / tenant.

Only uid and name are guaranteed; the rest of the payload varies by endpoint (login vs /companies vs SSO callback — see RESEARCH §3). On the /companies listing the server also returns:

  • user_params — free-form tenant settings (e.g. color, role, tag_allocation).
  • users — a list of member-user summaries (uid, email, names, timestamps).
  • projects — a list of project summaries scoped to the company.

users / projects are typed Any because their per-element shape differs across endpoints; read them as plain lists of dicts.

Role

Bases: str, Enum

Resolved user role.

Values are the server's accepted role strings. Ordering in this declaration is informational (most-privileged first) and does not imply comparable enums — compare by string equality only.

The boolean wire flags (isSuperAdmin etc.) and the raw role string both map into one of these members. See :attr:User.role for the resolution rules.

Capabilities

Bases: _BaseModel

SDK-detected server capabilities — SSO availability + API version.

Built by :meth:AuthAPI.capabilities. The sso flag reflects whether GET /auth/sso/log_in resolves; api_version is "2.1" when SSO is reachable, "2.0" otherwise (matches the JS reference client's fallback heuristic, RESEARCH §4 "Probe SSO support").

System

SystemHealth dataclass

SystemHealth(host=None, uptime=None, connections=None, partial=False, errors=dict())

Aggregated server-health snapshot.

Each sub-field is populated independently; a failure on one sub-call sets that field to None and records the exception in errors, leaving the rest of the snapshot intact (RESEARCH §4 "Refresh System-Health dashboard" — the JS client routes each error independently).

partial is True when any sub-call failed.

Host

Bases: _BaseModel

Host info — hostname + serverStatus (disk, memory, …).

The server_status field's shape varies by deployment and Linux distribution; surface it as a dict and let .raw carry the rest.

Uptime

Bases: _BaseModel

A row from GET /host/uptime — service name + uptime data.

Connection

Bases: _BaseModel

A row from GET /host/connections — active server connection.

Version

Bases: _BaseModel

Server version info from GET /version.

The live server emits a nested shape: {api: {version}, rtls_frontend: {version}, custom_product_attributes: {...}}. Older deployments may use the flat shape RESEARCH originally documented. All fields are optional; the convenience properties below pull the version string out of whichever form the server sent.

api_version_str property

api_version_str

Return the API version as a string, regardless of wire shape.

frontend_version_str property

frontend_version_str

Return the frontend version as a string, if available.

WsHost

Bases: _BaseModel

The hostname for the WebSocket endpoint, returned by GET /wshost/.

SystemSubscriber

Bases: _BaseModel

A system-event subscriber — distinct from alarm subscribers.

Returned under {systemNotificationSubscriber: [...]} from GET /system_notifications/.

WebSocket channels

WsChannelEnvelope

Bases: BaseModel

Base envelope. Discriminated by channel; raw keeps the wire dict.

from_wire classmethod

from_wire(data)

Build a typed channel message from the params.data payload.

Subclasses supply the channel literal as a default; this helper reads that default and injects raw=data so the original wire dict is always reachable via :attr:raw.

WsChannelMessage

Bases: WsChannelEnvelope

Fallback envelope for channels the SDK does not model.

Yielded by the WS iterator when (a) the channel name has no typed class registered or (b) a typed decode raised :class:pydantic.ValidationError. The full wire payload is on raw.

PositionMessage

Bases: WsChannelEnvelope

A position fix delivered over the pos channel.

Field list follows rtls-frontend/docs/public/api/websocket.md and subscriptions-manager/lib/models/notify/Notify.mjs:53-110. The deprecated legacy fields (id, locationId, sl, sfsn, to_type_id, er, slvr_mode, pe_dim, inactive, edge_corr) flow through .raw rather than as typed attributes.

NotifyMessage

Bases: WsChannelEnvelope

Object-lifecycle event broadcast on the notify channel.

Fields per websocket.md §"Notify fields". project_uid is documented as required but heartbeat frames (action == "HB") emit without it — kept optional so HB messages remain typed-decodable when callers opt in to receiving them.

AlarmMessage

Bases: WsChannelEnvelope

Alarm event delivered on the alarm channel.

Fields per websocket.md §"Alarm fields". state carries "set" / "clear"; the deprecated status mirror passes through .raw. The docs type x/y/z as strings but the wire example uses integers — wire wins.

ZoneEventMessage

Bases: WsChannelEnvelope

Zone enter/leave event broadcast on the zone_event channel.

Fields per websocket.md §"Zone event fields".

SensorMessage

Bases: WsChannelEnvelope

Sensor reading delivered on the sensor channel.

Server-side builder (Notify.mjs:180) only documents two pass-through identifier fields; the rest of the ZMQ payload flows through .raw for callers that need it.

OtaMessage

Bases: WsChannelEnvelope

OTA-firmware status broadcast on the ota channel.

Server-side builder (Notify.mjs:195) only requires mac to route the message; project_uid is derived from the MAC's project association. The full ZMQ payload flows through .raw.

ParamPMessage

Bases: WsChannelEnvelope

Parameter-publication broadcast on the param_p channel.

Fields per Notify.mjs:283 builder. plist is the published parameter payload — the contents vary by device firmware and are treated as an opaque dict.

UserMsgMessage

Bases: WsChannelEnvelope

User-message broadcast on the user_msg channel.

Companion of the REST POST /api/v2/user_msg/ send path. Wire example: rtls-frontend/server/routes/user_msg/example.json. hex is up to 512 hex characters (256 binary bytes).

Notifications & subscribers

NotificationProfile

Bases: _BaseModel

A notification profile: rules for emitting alerts on alarms.

NotificationType

Bases: _BaseModel

One entry from GET /notification_types.

The live server returns this list as bare strings (e.g. "zone_violation"); some deployments use objects. The model accepts either form — a string is wrapped as NotificationType(name=...).

Subscriber

Bases: _BaseModel

An alarm-notifications subscriber.

Returned under {subscribers: [...]} from GET /subscribers/.

SubscriberAddresses

Bases: _BaseModel

Contact addresses on a subscriber profile.

The server emits these fields as either a list of strings or a bare string depending on the endpoint / version (RESEARCH §1 documented arrays; the live server has been observed returning strings). The model coerces both shapes into a list of strings so callers don't have to branch.

TDOA configuration

Schedule

Bases: _BaseModel

A TDOA schedule definition.

content is the serialized per-slot table (format depends on tdoa_version). Use :attr:raw for any fields the SDK doesn't surface yet.

LsbSettings

Bases: _BaseModel

LSB sync configuration for a project.

The config nested under values is surfaced as top-level members on read — the scalars (active_schedule_uid, autostart, configured_state, lsb_version / tdoa_version fallbacks) and the typed masters / distributors lists — and the raw values blob is dropped. The members round-trip back into values on :meth:LsbAPI.save.

Mutable (unlike the SDK's other models): edit a member then call client.lsb.save(settings).

Master

Bases: _BaseModel

One master row (values.masters[]).

Distributor

Bases: _BaseModel

One distributor row (values.distributors[]).

LsbStatus

Bases: str, Enum

Computed running state of LSB sync for a project.

Returned by :meth:LsbAPI.status. STARTED means the set of nodes currently reporting as master matches the configured masters; STOPPED means no node is reporting as master; UNKNOWN is any in-between / mismatched state.

NetworkSettings

Bases: _BaseModel

Per-project network configuration.

ServiceMode

Bases: _BaseModel

Service-mode toggle for a project's devices.

devices_in_service_mode is sometimes returned as null on deployments that haven't activated service mode yet; coerce to an empty list so the typed surface stays clean.

NodeParams

Bases: _BaseModel

One row of a hw_params.json response.

Field shapes per parameter type (when not None):

  • sf: {"value": {"addr", "sfd", "bcs", "tdoas", "bsd", "tsd"}, "elapsed": int}
  • sync_slot: {"value": {"addr": str, "slot": int}, "elapsed": int}
  • nwk_cfg: {"value": {"router": bool}, "elapsed": int}

Other parameter types (tdoa_schedule, tdoa_schd, srv_mode, p, …) ride on extra="allow" and are accessible via :attr:raw.

Bulk operation results

BulkResult dataclass

BulkResult(succeeded=list(), failures=list())

Bases: Generic[T]

Result of a continue-on-error bulk operation.

The SDK accumulates successes and failures separately so the caller can react to a partial outcome without parsing exception messages.

Examples:

>>> result = client.tags.bulk_delete(["t-1", "t-missing", "t-3"])
>>> result.success_count, result.failure_count
(2, 1)
>>> result.failures[0].error
NotFound('trackable t-missing not found')

__bool__

__bool__()

Truthy when nothing failed.

Lets callers write if result: ... for the happy path.

BulkFailure dataclass

BulkFailure(error, input=None, uid=None)

One row that failed inside a bulk operation.

Attributes:

Name Type Description
error RtlsError

The typed SDK exception that fired for this row.

input Any

Whatever the caller passed for this row (e.g. a uid string, a dict of fields). Useful for retrying the failed subset.

uid str | None

Convenience accessor when input is itself a uid.

ImportResult dataclass

ImportResult(valid=list(), invalid=list(), duplicate=list(), imported_count=0, failures=list())

Result of a CSV-style import operation.

Attributes:

Name Type Description
valid list[str]

MAC addresses the server's validation endpoint accepted as new.

invalid list[str]

MAC addresses rejected as malformed or otherwise invalid.

duplicate list[str]

MAC addresses already present in the server.

imported_count int

Number of nodes actually created during the per-row loop. Equal to len(valid) - len(failures) after a successful run.

failures list[BulkFailure]

Per-row failures (only populated when validation succeeded but subsequent nodes.create calls failed for individual rows).