roboto.domain.devices#

Device management for the Roboto platform.

This module provides functionality for managing devices - non-human entities that can interact with Roboto on behalf of organizations. Devices are typically robots or other systems that upload data to the platform.

The main components are: - Device: Core device entity for registration, token management, and operations - DeviceRecord: Wire-transmissible representation of device data - CreateDeviceRequest: Request payload for device registration - UpdateDeviceRequest: Request payload for updating a device

Devices are identified by unique device IDs within their organization and can be assigned API tokens for authentication. They serve as the primary mechanism for automated data ingestion and platform interaction.

Submodules#

Package Contents#

class roboto.domain.devices.CreateDeviceRequest(/, **data)#

Bases: pydantic.BaseModel

Request payload to create a new device.

This request is used to register a new device with the Roboto platform. The device will be associated with the specified organization and can subsequently be used for authentication and data operations.

Parameters:

data (Any)

custom_fields: dict[str, Any] | None = None#

Initial values for Ready custom fields on this device.

Each key must be the name of a CustomField that is Ready for the caller’s org and the Device entity type; each value must satisfy the field’s declared type. Names that are undefined or not Ready, and values that don’t match the field’s type, are rejected with a structured error.

device_id: str#

A user-provided identifier for a device, which is unique within that device’s org.

device_type_id: str | None = None#

ID (dt_...) of a device type in the device’s org to assign to the device. None leaves the device without a type.

metadata: dict[str, Any]#

Key-value metadata pairs to associate with the device for discovery and search.

org_id: str | None = None#

The org to which this device belongs. If None, the device will be registered under the caller’s organization (if they belong to only one org) or an error will be raised if the caller belongs to multiple organizations.

tags: list[str]#

List of tags for device discovery and organization.

class roboto.domain.devices.Device(record, roboto_client=None)#

A device is a non-human entity that can interact with Roboto on behalf of an organization.

Devices represent robots, systems, or other non-human entities that need to authenticate and interact with the Roboto platform. Each device is uniquely identified by a device_id within its organization and can be assigned API tokens for secure authentication.

Common kinds of device include:

  • Robots that upload log data directly from their onboard software

  • Automated upload stations that collect and transmit data from multiple sources

  • Edge computing devices that process and forward data to Roboto

Devices are associated with Org entities and can create Token objects for authentication. The underlying data is a DeviceRecord, the raw record as returned by Roboto.

Device IDs are typically meaningful identifiers like serial numbers, asset tags, or other organization-specific naming schemes that help identify the physical or logical entity in the real world.

Note

Devices cannot be instantiated directly through the constructor. Use the class methods create(), from_id(), get_or_create(), or for_org() to obtain Device instances.

Parameters:
clear_custom_field(name)#

Clear a single custom-field value on this device to None.

Parameters:

name (str)

Return type:

Device

clear_custom_fields(names)#

Clear multiple custom-field values on this device to None.

Parameters:

names (collections.abc.Sequence[str])

Return type:

Device

property configurations: roboto.domain.files.FileSystem#

Files specific to this device.

Configurations hold files such as URDFs, calibration files, manifests, and maintenance logs. Data the device records goes to datasets instead: a dataset file whose device_id names this device is not one of its configurations.

A device with a device type also inherits the type’s configurations. Pass include_inherited=True to list_files() or download_files() to include them; the other methods act on the device’s own files only.

Raises:

RobotoNotReadyException – The device has no universal_device_id.

Return type:

roboto.domain.files.FileSystem

classmethod create(device_id, metadata=None, tags=None, custom_fields=None, caller_org_id=None, roboto_client=None, device_type_id=None)#

Register a new device with the Roboto platform.

Creates a new device entity that can authenticate and interact with Roboto on behalf of the specified organization. The device_id must be unique within the organization.

Parameters:
  • device_id (str) – A user-provided identifier for the device, unique within the organization. This is typically a meaningful identifier like a serial number, asset tag, or other organization-specific naming scheme.

  • metadata (Optional[dict[str, Any]]) – Optional key-value pairs to associate with the device for discovery and search. For example: {“model”: “mk2”, “serial_number”: “SN001234”}.

  • tags (Optional[list[str]]) – Optional list of tags to associate with the device for discovery and organization. For example: [“production”, “warehouse-a”].

  • custom_fields (Optional[dict[str, Any]]) – Optional initial values for Ready custom fields defined on Devices in the caller’s org. Keys must match Ready field names; values must satisfy each field’s declared type.

  • caller_org_id (Optional[str]) – The organization ID to register the device under. If not specified and the caller belongs to only one organization, that organization will be used. Required if the caller belongs to multiple organizations.

  • roboto_client (Optional[roboto.http.RobotoClient]) – Optional RobotoClient instance for API communication. If not provided, the default client configuration will be used.

  • device_type_id (Optional[str]) – Optional dt_ ID of a DeviceType in the same organization to assign to the device.

Returns:

A Device instance representing the newly registered device.

Raises:
  • RobotoConflictException – If a device with the same device_id already exists in the specified organization.

  • RobotoUnauthorizedException – If the caller lacks permission to create devices in the specified organization.

  • RobotoInvalidRequestException – If the device_id is invalid, the organization ID is malformed, or device_type_id is given and the organization has no such device type or does not have device types enabled.

Return type:

Device

Examples

Register a robot device:

>>> device = Device.create(device_id="robot_001", caller_org_id="og_abc123")
>>> print(f"Registered device: {device.device_id}")
Registered device: robot_001

Register an upload station:

>>> device = Device.create(device_id="upload_station_alpha")
>>> print(f"Device org: {device.org_id}")
Device org: og_xyz789
create_session(name, description=None, metadata=None, tags=None, custom_fields=None, anchor=None, files=None)#

Create one Session on this Device, optionally with its files, topics, and schemas.

The one-session form of create_sessions(), taking a single declaration’s fields as arguments and sharing its semantics: the Session, its file attachments, its topics, and its time ranges are created together or not at all, and every file the declaration names must already be uploaded. name identifies the Session within this Device, so resending the same call is safe; the platform reuses the Session already registered under that name instead of creating a second one. Arguments left at their defaults are left out of the request, so a call that reuses an existing Session never overwrites attributes it does not name. To create a Session with no name, or one spanning several Devices, use create().

A declaration the platform refuses raises here. Only create_sessions() reports a refusal instead of raising it, because only a batch has positions to trace refusals back to.

Declared times are stored exactly as given, in each file’s own timestamps, and read as nanoseconds since the Unix epoch; the platform never invents a wall-clock time. To place the Session at the wall-clock time it happened, supply anchor, or call set_unix_offset() later.

Parameters:
  • name (str) – Name of the Session, unique within this Device (max 120 characters).

  • description (Optional[str]) – Optional description of the Session.

  • metadata (Optional[dict[str, Any]]) – Optional initial metadata. Sessions are not filterable or sortable by metadata keys; for queryable structured attributes, define a custom field on the Session entity type.

  • tags (Optional[collections.abc.Sequence[str]]) – Optional initial tags. Sessions can be filtered by tag membership but are not sortable by tag.

  • custom_fields (Optional[dict[str, Any]]) – Optional initial values for Ready custom fields defined on Sessions in this Device’s org. Keys must match Ready field names; values must satisfy each field’s declared type.

  • anchor (Optional[roboto.time.Time]) – Optional wall-clock anchor, the real-world instant at which the declared data’s time 0 occurred. An int is nanoseconds since the Unix epoch; any other Time is read as to_epoch_nanoseconds() reads it (a datetime or ISO 8601 string is that instant; a float, Decimal, or numeric string is seconds since the epoch). It applies to every file entry that does not carry its own anchor_ns.

  • files (Optional[collections.abc.Sequence[roboto.experimental.sessions.SessionFile]]) – Files composing this Session, with the topics whose data each one carries. Every file must already be uploaded, and may appear at most once. Files can also be included after creation with add_file() or add_files().

Returns:

The created Session.

Raises:
  • TypeError – If anchor is not one of the Time types.

  • ValueError – If anchor is a boolean, a negative number (an int, float, Decimal, or numeric string), or a string that is neither a number of seconds nor an ISO 8601 timestamp. Raised before anything is sent to the platform.

  • OverflowError – If anchor is an infinite float, Decimal, or string, such as "inf". Raised before anything is sent to the platform.

  • pydantic.ValidationError – If name is empty or longer than 120 characters, anchor does not fall after the Unix epoch or is too large for a signed 64-bit integer of nanoseconds, a file appears in more than one entry, files declares more than MAX_FILES_AND_TOPICS_PER_REQUEST files and topics combined, or representations name one file in two storage formats. Raised while the request is being built, before anything is sent to the platform.

  • RobotoInvalidRequestException – If the platform refuses the declaration, either because it contradicts data the platform already holds or because it carries a value the platform rejects, such as a custom_fields value that does not satisfy its field’s declared type. No Session, file attachment, topic, or time range is created; the topic identifiers and schema definitions the declaration resolved stay stored, and a resend reuses them.

  • RobotoConflictException – If something the declaration was prepared against changed while it was being written. Nothing is created; resending is the fix.

  • RobotoNotFoundException – If this Device is no longer registered, the file_id of a file entry, or of a representation one of its topics lists, does not name a file in this Device’s organization whose status is Available, or the declaration names something else that does not exist, such as a custom_fields key naming a custom field the organization does not define on Sessions. Nothing is created.

  • RobotoUnauthorizedException – If the caller lacks permission to create Sessions on this Device, or to edit a file the declaration declares topics on, a file it anchors, or a file a listed representation names, or lacks topic edit access in this Device’s organization while the declaration states is_default_for_reads on a timeline source.

  • RobotoUnrecognizedErrorException – If the platform refuses the declaration under an error code this SDK release does not define. Carries the code and message the platform sent.

Return type:

roboto.experimental.sessions.Session

Examples

Create a Session and add a file to it:

>>> device = Device.from_id("robot_001", org_id="og_abc123")
>>> session = device.create_session(name="2024-05-01_morning_run")
>>> session.add_file("fl_0123456789abcdef")

Create a Session placed at the wall-clock time it was recorded:

>>> import datetime
>>> session = device.create_session(
...     name="2024-05-01_morning_run",
...     anchor=datetime.datetime(2024, 5, 1, 9, 30, tzinfo=datetime.timezone.utc),
... )
create_sessions(sessions)#

Create many Sessions on this Device, each with its files, topics, and schemas, in one call.

Each call accepts up to MAX_SESSIONS_PER_REQUEST declarations, one per Session (e.g. the episodes of a LeRobot dataset), and up to MAX_FILES_AND_TOPICS_PER_REQUEST files and topics combined, counted across every declaration; split anything larger across several calls. Every file a declaration names must already be uploaded; upload_files() returns the file IDs it creates, and files from any number of datasets may appear in one batch.

The platform decides which declarations to refuse before writing anything, then writes the rest together. A declaration’s Session, file attachments, topics, and time ranges are created together or not at all, and a declaration the platform refuses leaves the others written as if it were absent. None of the declarations is written when a failure the platform did not anticipate, such as a timeout, interrupts the call, or when a Session a declaration reuses is deleted before the call completes, which raises RobotoNotFoundException. Each declaration is written as it would be had the ones before it been sent as calls of their own: a later declaration anchoring data an earlier one holds moves the earlier Session’s time range with it. A refused declaration, or a call that fails, still leaves behind the topic identifiers and schema definitions it resolved, which a resend reuses.

Check failed before treating the batch as done. Each entry there is the RobotoDomainException the platform refused a declaration with, so isinstance tells the reasons apart; a refusal under an error code this SDK release does not define arrives as RobotoUnrecognizedErrorException.

Resending the same call is safe. This Device plus each declaration’s name identifies the Session the declaration creates or reuses, so a resend fills in only what is missing rather than duplicating what an earlier attempt created.

Declared times are stored exactly as given, in each file’s own timestamps, and read as nanoseconds since the Unix epoch; the platform never invents a wall-clock time. A recording whose timestamps start at 0 therefore sits at the epoch until it is anchored. To place a Session at the wall-clock time it happened, supply anchor_ns, or call set_unix_offset() later.

Parameters:

sessions (collections.abc.Sequence[roboto.experimental.sessions.SessionDeclaration]) – One declaration per Session to create. An empty sequence returns an empty response without contacting the platform.

Returns:

A BatchResponse with one element per declaration, in request order, holding either the Session the declaration created or why the platform refused it. A declaration naming a Session this Device already holds yields that Session rather than a second one.

Raises:
  • pydantic.ValidationError – If more than MAX_SESSIONS_PER_REQUEST declarations are given, the batch declares more than MAX_FILES_AND_TOPICS_PER_REQUEST files and topics combined, the same session name is declared more than once, or representations name one file in two storage formats. All are enforced when the request body is constructed, before anything is sent to the platform.

  • RobotoInvalidRequestException – If the batch is malformed. Nothing is created.

  • RobotoNotFoundException – If this Device is no longer registered, the file_id of a file entry, or of a representation one of its topics lists, does not name a file in this Device’s organization whose status is Available, or a Session a declaration reuses is deleted before the call completes. Nothing is created.

  • RobotoUnauthorizedException – If the caller lacks permission to create Sessions on this Device, or to edit a file a declaration declares topics on, a file it anchors, or a file a listed representation names, or lacks topic edit access in this Device’s organization while a declaration states is_default_for_reads on a timeline source.

Return type:

roboto.http.BatchResponse[roboto.experimental.sessions.Session]

Examples

Register two chunks of one recording as a single Session, each chunk’s topic data read from the chunk itself:

>>> import pathlib
>>> from roboto.domain.datasets import Dataset
>>> from roboto.domain.devices import Device
>>> from roboto.domain.topics import CanonicalDataType, RepresentationStorageFormat
>>> from roboto.experimental.ingest import (
...     Field,
...     McapLogTimeSource,
...     RepresentationDeclaration,
...     Schema,
...     TopicDeclaration,
... )
>>> from roboto.experimental.sessions import SessionDeclaration, SessionFile
>>> imu_schema = Schema(
...     name="sensor_msgs/msg/Imu",
...     fields=[
...         Field(
...             name="angular_velocity_x",
...             data_type="float64",
...             canonical_data_type=CanonicalDataType.Number,
...         ),
...     ],
... )
>>> dataset = Dataset.from_id("ds_0123456789ab")
>>> device = Device.from_id("robot_001")
>>> chunks = [pathlib.Path("recording/chunk_0000.mcap"), pathlib.Path("recording/chunk_0001.mcap")]
>>> file_ids = dataset.upload_files(chunks)
>>> batch = device.create_sessions(
...     [
...         SessionDeclaration(
...             name="morning_drive",
...             files=[
...                 SessionFile(
...                     file_id=file_ids[chunks[0]],
...                     topics=[
...                         TopicDeclaration(
...                             topic_name="/imu",
...                             topic_schema=imu_schema,
...                             timeline_sources=[
...                                 McapLogTimeSource(
...                                     min_file_timestamp_ns=1_785_974_400_000_000_000,
...                                     max_file_timestamp_ns=1_785_974_404_000_000_000,
...                                 ),
...                             ],
...                             representations=[
...                                 RepresentationDeclaration(
...                                     file_id=file_ids[chunks[0]],
...                                     storage_format=RepresentationStorageFormat.MCAP,
...                                 ),
...                             ],
...                         ),
...                     ],
...                 ),
...                 SessionFile(
...                     file_id=file_ids[chunks[1]],
...                     topics=[
...                         TopicDeclaration(
...                             topic_name="/imu",
...                             topic_schema=imu_schema,
...                             timeline_sources=[
...                                 McapLogTimeSource(
...                                     min_file_timestamp_ns=1_785_974_404_000_000_000,
...                                     max_file_timestamp_ns=1_785_974_408_000_000_000,
...                                 ),
...                             ],
...                             representations=[
...                                 RepresentationDeclaration(
...                                     file_id=file_ids[chunks[1]],
...                                     storage_format=RepresentationStorageFormat.MCAP,
...                                 ),
...                             ],
...                         ),
...                     ],
...                 ),
...             ],
...         ),
...     ],
... )
>>> batch.failed
[]
create_token(expiry_days=366, name=None, description=None, api_scopes=None)#

Create an authentication token for this device.

Generates a new API token that can be used to authenticate requests made on behalf of this device. The token secret is returned only once and cannot be retrieved again, so it must be stored securely by the caller.

Parameters:
  • expiry_days (int) – Number of days until the token expires. Defaults to 366 days (1 year). Must be a positive integer.

  • name (Optional[str]) – Human-readable name for the token. If not provided, defaults to “{org_id}_{device_id}” format.

  • description (Optional[str]) – Optional description explaining the token’s purpose or usage context.

  • api_scopes (Optional[collections.abc.Collection[roboto.auth.scope.ApiScope]]) – Optional set of API scopes to limit the token’s permissions. If not provided, the token will have full access to all APIs.

Returns:

  • Token: The Token object representing the created token

  • str: The secret token value (only available at creation time)

Return type:

A tuple containing

Raises:
  • RobotoDomainException – If token creation fails or the secret is not returned by the server (this should never happen under normal circumstances).

  • RobotoUnauthorizedException – If the caller lacks permission to create tokens for this device.

Examples

Create a token with default settings:

>>> device = Device.from_id("robot_001", org_id="og_abc123")
>>> token, secret = device.create_token()
>>> print(f"Token created: {token.token_id}")
>>> print(f"Secret (save this!): {secret}")
Token created: to_abc123def456
Secret (save this!): robo_pat_abc123def456...

Create a token with custom expiry and description:

>>> token, secret = device.create_token(
...     expiry_days=30, name="Monthly Upload Token", description="Token for automated monthly data uploads"
... )
>>> print(f"Token expires in 30 days: {token.token_id}")
Token expires in 30 days: to_def789ghi012
property created: datetime.datetime#

The timestamp when this device was registered with Roboto.

Return type:

datetime.datetime

property created_by: str#

The user ID of the person who registered this device.

Return type:

str

property custom_fields: dict[str, Any]#

Custom-field values defined on Devices in this org.

Every Ready CustomField defined for (org_id, Device) appears as a key. Values that have not been set on this device surface as None rather than being absent. Empty when no custom fields are defined for the org.

A Timestamp value is returned as an ISO 8601 string.

Return type:

dict[str, Any]

delete(keep_configurations=False)#

Delete this device from the Roboto platform.

Permanently removes this device, its configurations, and all associated tokens. This action cannot be undone. Any tokens created for this device will be immediately invalidated.

The device’s configurations (configurations) are deleted with it, every version of each file, shortly after this call returns. With keep_configurations=True they move to the org’s assets (assets) instead, under devices/<universal_device_id>/, keeping their file IDs and every version. Links among the device’s configurations are deleted in both cases, and the files they point at are left alone.

Shortly after this call returns, this device’s ID is also cleared from the device_id of the org’s datasets and of every version of the org’s files, unless a device with the same ID is registered in the org before then.

Parameters:

keep_configurations (bool) – Move the device’s configurations to the org’s assets instead of deleting them. Requires permission to upload files to the org.

Raises:
Return type:

None

Examples

Delete a device after confirming its identity:

>>> device = Device.from_id("old_robot_001")
>>> print(f"Deleting device: {device.device_id}")
>>> device.delete()
>>> print("Device deleted successfully")
Deleting device: old_robot_001
Device deleted successfully

Delete a device but keep its configurations in the org’s assets:

>>> device = Device.from_id("old_robot_001")
>>> device.delete(keep_configurations=True)
property device_id: str#

This device’s ID. Device ID is a user-provided identifier for a device, which is unique within the device’s org.

Return type:

str

property device_type_id: str | None#

The dt_ ID of the device type assigned to this device, or None when it has no type.

Return type:

Optional[str]

property encoded_device_id: str#

The device ID, URL-encoded. This is useful for constructing URLs to Roboto APIs which contain the device ID.

Return type:

str

classmethod for_org(org_id, roboto_client=None)#

List all devices registered for a given organization.

Retrieves all devices that belong to the specified organization. For organizations with large numbers of devices, this method uses pagination and yields results as they become available from the API.

Parameters:
  • org_id (str) – The organization ID to list devices for.

  • roboto_client (Optional[roboto.http.RobotoClient]) – Optional RobotoClient instance for API communication. If not provided, the default client configuration will be used.

Returns:

A generator of Device objects. For organizations with many devices, this may involve multiple service calls, and the generator will yield results as they become available.

Raises:
Return type:

collections.abc.Generator[Device, None, None]

Examples

List all devices in an organization:

>>> for device in Device.for_org("og_abc123"):
...     print(f"Device: {device.device_id} (created: {device.created})")
Device: robot_001 (created: 2024-01-15 10:30:00)
Device: upload_station_beta (created: 2024-01-17 09:15:00)

Count devices in an organization:

>>> device_count = sum(1 for _ in Device.for_org("og_abc123"))
>>> print(f"Total devices: {device_count}")
Total devices: 2
classmethod from_id(device_id, roboto_client=None, org_id=None)#

Retrieve a device by its device ID.

Looks up and returns a Device instance for the specified device_id. The device_id must be unique within the organization scope.

Parameters:
  • device_id (str) – The device ID to look up. This is the user-provided identifier that was specified when the device was created.

  • roboto_client (Optional[roboto.http.RobotoClient]) – Optional RobotoClient instance for API communication. If not provided, the default client configuration will be used.

  • org_id (Optional[str]) – The organization ID that owns the device. If not specified and the caller belongs to only one organization, that organization will be used. Required if the caller belongs to multiple organizations.

Returns:

A Device object representing the specified device.

Raises:
Return type:

Device

Examples

Retrieve a device by ID with explicit organization:

>>> device = Device.from_id("robot_001", org_id="og_abc123")
>>> print(f"Device: {device.device_id} in org {device.org_id}")
Device: robot_001 in org og_abc123

Retrieve a device:

>>> device = Device.from_id("upload_station_alpha")
>>> print(f"Found device created by: {device.created_by}")
Found device created by: user@example.com
classmethod get_or_create(device_id, metadata=None, tags=None, custom_fields=None, caller_org_id=None, roboto_client=None)#

Register a device, or return the existing one if device_id is already taken.

metadata, tags, and custom_fields are applied only by the call that registers the device; a device that is already registered comes back unchanged.

Parameters:
  • device_id (str) – A user-provided identifier for the device, unique within the organization.

  • metadata (Optional[dict[str, Any]]) – Optional key-value pairs to associate with the device on first registration.

  • tags (Optional[list[str]]) – Optional tags to associate with the device on first registration.

  • custom_fields (Optional[dict[str, Any]]) – Optional initial values for Ready custom fields, applied on first registration.

  • caller_org_id (Optional[str]) – The organization the device belongs to. Required if the caller belongs to multiple organizations.

  • roboto_client (Optional[roboto.http.RobotoClient]) – Optional RobotoClient instance for API communication.

Returns:

The newly registered or pre-existing Device.

Raises:
  • RobotoUnauthorizedException – If the caller lacks permission to create devices in, or read devices from, the specified organization.

  • RobotoInvalidRequestException – If the device_id is invalid or the organization ID is malformed.

  • RobotoNotFoundException – If the device is deleted between the registration attempt and the lookup that follows it. Those are two calls rather than one atomic operation, so the race is possible, though unlikely.

Return type:

Device

Examples

>>> device = Device.get_or_create(device_id="aloha_001")
>>> device.device_id
'aloha_001'
list_sessions()#

Iterate all Sessions attached to this Device.

Yields results as they are returned from the server, paginating transparently.

Examples

Print the name of every Session for a Device:

>>> device = Device.from_id("robot_001", org_id="og_abc123")
>>> for session in device.list_sessions():
...     print(session.name)
Return type:

collections.abc.Generator[roboto.experimental.sessions.Session, None, None]

property metadata: dict[str, Any]#

Key-value metadata pairs associated with this device.

Return type:

dict[str, Any]

property modified: datetime.datetime#

The timestamp when this device record was last modified.

Return type:

datetime.datetime

property modified_by: str#

The user ID of the person who last modified this device record.

Return type:

str

property org_id: str#

The ID of the org to which this device belongs.

Return type:

str

put_metadata(metadata)#

Add or update metadata fields for this device.

Parameters:

metadata (dict[str, Any]) – Key-value pairs to add or update in the device’s metadata. Existing keys will be overwritten, new keys will be added.

Returns:

Updated Device instance with the new metadata.

Return type:

Device

Example

>>> device = Device.from_id("robot_001")
>>> updated_device = device.put_metadata({"firmware_version": "2.1.0", "location": "warehouse-b"})
>>> print(updated_device.metadata["firmware_version"])
2.1.0
put_tags(tags)#

Add tags to this device.

Parameters:

tags (list[str]) – List of tags to add to the device. Duplicate tags will be ignored.

Returns:

Updated Device instance with the new tags added.

Return type:

Device

Example

>>> device = Device.from_id("robot_001")
>>> updated_device = device.put_tags(["production", "warehouse-c"])
>>> print("production" in updated_device.tags)
True
property record: roboto.domain.devices.record.DeviceRecord#

Underlying DeviceRecord for this device.

This is the raw record as returned by Roboto and may evolve over time; prefer the public Device API unless you need direct access to the record.

Return type:

roboto.domain.devices.record.DeviceRecord

remove_metadata(keys)#

Remove metadata fields from this device.

Parameters:

keys (list[str]) – List of metadata keys to remove from the device.

Returns:

Updated Device instance with the specified metadata keys removed.

Return type:

Device

Example

>>> device = Device.from_id("robot_001")
>>> updated_device = device.remove_metadata(["old_field", "deprecated_key"])
remove_tags(tags)#

Remove tags from this device.

Parameters:

tags (list[str]) – List of tags to remove from the device.

Returns:

Updated Device instance with the specified tags removed.

Return type:

Device

Example

>>> device = Device.from_id("robot_001")
>>> updated_device = device.remove_tags(["old_tag", "deprecated"])
set_custom_field(name, value)#

Set a single custom-field value on this device.

name must be the name of a Ready custom field for this device’s org and the Device entity type; value must satisfy the field’s declared type.

Parameters:
  • name (str)

  • value (Any)

Return type:

Device

set_custom_fields(fields)#

Set or overwrite multiple custom-field values on this device.

Each key must name a Ready custom field for this device’s org and the Device entity type; each value must satisfy the field’s declared type.

Parameters:

fields (dict[str, Any])

Return type:

Device

set_device_type(device_type_id)#

Assign a device type to this device, or remove its type.

The device’s own configurations are unaffected. Listing them with include_inherited=True afterwards includes the new type’s configurations, or only the device’s own when the type was removed.

Parameters:

device_type_id (Optional[str]) – dt_ ID of a DeviceType in this device’s organization, or None to leave the device without a type.

Returns:

Updated Device instance with the new device type.

Raises:

RobotoInvalidRequestException – device_type_id is not None and the organization has no such device type or does not have device types enabled.

Return type:

Device

Examples

>>> device = Device.from_id("robot_001")
>>> device.set_device_type("dt_abc123").device_type_id
'dt_abc123'
>>> device.set_device_type(None).device_type_id is None
True
property tags: list[str]#

List of tags associated with this device.

Return type:

list[str]

tokens()#

Retrieve all authentication tokens associated with this device.

Returns a list of all tokens that have been created for this device, including both active and expired tokens. The token secrets are not included in the response as they are only available at creation time.

Returns:

A sequence of Token objects representing all tokens created for this device. The sequence may be empty if no tokens have been created.

Raises:

RobotoUnauthorizedException – If the caller lacks permission to list tokens for this device.

Return type:

collections.abc.Sequence[roboto.domain.tokens.Token]

Examples

List all tokens for a device:

>>> device = Device.from_id("robot_001")
>>> tokens = device.tokens()
>>> for token in tokens:
...     print(f"Token: {token.token_id}")
Token: to_abc123def456
Token: to_ghi789jkl012

Check if device has any tokens:

>>> device = Device.from_id("new_robot")
>>> if device.tokens():
...     print("Device has tokens")
... else:
...     print("No tokens found for device")
No tokens found for device
update(request)#

Update device properties using a structured request.

Parameters:

request (roboto.domain.devices.operations.UpdateDeviceRequest) – UpdateDeviceRequest containing the changes to apply.

Returns:

Updated Device instance with the changes applied.

Return type:

Device

Example

>>> from roboto.updates import MetadataChangeset
>>> device = Device.from_id("robot_001")
>>> updated_device = device.update(
...     UpdateDeviceRequest(
...         metadata_changeset=MetadataChangeset(
...             put_fields={"version": "2.0"}, put_tags=["updated"], remove_tags=["old"]
...         )
...     )
... )
class roboto.domain.devices.DeviceRecord(/, **data)#

Bases: pydantic.BaseModel

A wire-transmissible representation of a device.

This record contains all the essential information about a device that can be transmitted over the network. It includes metadata about when the device was created and modified, along with its organizational association.

Parameters:

data (Any)

created: datetime.datetime#

Date/time when this device was registered.

created_by: str#

The user who registered this device.

custom_fields: dict[str, Any]#

Values for the custom fields defined on Devices in this org.

Every Ready custom field defined for (org_id, Device) appears as a key; values that have not been set surface as None rather than being absent. Empty when no custom fields are defined for the org.

device_id: str#

A user-provided identifier for a device, which is unique within that device’s org.

device_type_id: str | None = None#

ID (dt_...) of the device type assigned to this device, or None when it has no type.

metadata: dict[str, Any]#

Key-value metadata pairs associated with this device.

modified: datetime.datetime#

Date/time when this device record was last modified.

modified_by: str#

The user who last modified this device record.

org_id: str#

The org to which this device belongs.

tags: list[str]#

List of tags associated with this device.

universal_device_id: str | None = None#

A Roboto-assigned identifier for a device (dv_...), unique across all orgs and never changed.

Use this ID, not device_id, with device(). None only when talking to a Roboto deployment that predates it.

class roboto.domain.devices.UpdateDeviceRequest(/, **data)#

Bases: pydantic.BaseModel

Request payload for updating device properties.

Used to modify a device’s metadata, tags, custom fields, and device type. Supports granular updates through metadata changesets that can add, update, or remove specific fields and tags without affecting other properties.

Parameters:

data (Any)

custom_fields_changeset: roboto.updates.CustomFieldChangeset | None = None#

Changes to apply to Ready custom-field values on this device.

Each referenced field name must be a Ready custom field for this device’s org and the Device entity type; each set_fields value must satisfy the field’s declared type. Names that are undefined or not Ready are rejected with a structured error. Field names not mentioned by the changeset are left unchanged.

device_type_id: str | roboto.sentinels.NotSetType | None#

ID (dt_...) of a device type in the device’s org to assign to the device. Set to None to remove the device’s type; the default, NotSet, leaves it unchanged.

metadata_changeset: roboto.updates.MetadataChangeset | None = None#

Metadata changes to apply (add, update, or remove fields/tags).

model_config#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].