UniteLabs
Concepts

Action

The callable primitive on a module. Every action has a type — Property, Sensor, or Control — that determines how you consume it.

An action is anything you can call on a module. Reading the current temperature, subscribing to a file-system change stream, triggering a centrifuge spin — these are all actions. Every action has a type that tells you how to consume it:

  • Property — read a single scalar value and complete
  • Sensor — subscribe to a stream of values
  • Control — trigger a change, optionally with parameters, and get a result

The platform UI groups actions into three sections by type. The SDK and REST API expose the type as a field on each action. See the Terminology table for the cross-surface mapping.

Property actions

A Property reads a single current value and returns it. Typical examples: server metadata (name, version, UUID), a folder whitelist, a balance's current stable weight.

# Read the connector's server name
name = await file_system.sila_service.server_name()
print(name)  # "FileSystem"

# Read a structured value — the folder whitelist
whitelist = await file_system.folder_service.folder_whitelist()
print(whitelist)  # ['/opt/fs_demo']

In the REST API a Property is invoked via /v1/data/{actionId} with Accept: application/json — the endpoint returns the current value and completes.

Sensor actions

A Sensor exposes a stream of values that change over time. Typical examples: a file-system change watcher, a live temperature feed, weight readings while a sample is being dispensed.

# Subscribe to file-system changes
async for change in file_system.folder_service.changes():
    print(change)

Consuming a Sensor holds open a long-lived stream. See Subscription for lifecycle, reliability, and cancellation. In the REST API, Sensors are consumed via /v1/data/{actionId} with Accept: text/event-stream, or registered as explicit subscriptions via /v1/subscriptions.

Control actions

A Control triggers a change on the instrument. Typical examples: deleting a file, setting a server name, setting a target temperature, starting a run, opening a door.

# Trigger a file deletion
await file_system.file_service.delete_file(path="/opt/fs_demo/old.txt")

# Set a parameter the server remembers afterward
await file_system.sila_service.set_server_name(server_name="FileSystem (Bench 3)")

Some Controls complete immediately; others run long enough to emit progress updates before returning their final result. Long-running Controls are consumed as subscriptions — the stream delivers status updates and optional intermediate results until the command completes.

Discovering actions and their types

Ask a module what actions it has, then read each action's type:

module = file_system.folder_service
print(module.actions.keys())
# dict_keys(['folder_whitelist', 'changes', 'get_folder_contents'])

for name, action in module.actions.items():
    print(name, action.type)
# folder_whitelist     PROPERTY
# changes              SENSOR
# get_folder_contents  CONTROL

From the REST API:

# All actions on a module — each record carries its type
curl "$BASE_URL/v1/modules/$MODULE_ID/actions" \
  -H "Authorization: Bearer $TOKEN"

# One action in detail — type, parameters, responses, source (SiLA URI)
curl "$BASE_URL/v1/actions/$ACTION_ID" \
  -H "Authorization: Bearer $TOKEN"

Each action record includes its type (one of PROPERTY, SENSOR, CONTROL), a human-readable name, typed parameters and responses schemas, and a source field that identifies the underlying SiLA URI for connector developers who need it.

Introspection: parameters and responses

Every action carries typed schemas describing what it expects and what it returns. The schema is programmatically accessible so you can build UIs, validate payloads before sending them, or generate client code:

# What parameters does this Control accept?
print(await file_system.sila_service.set_server_name.parameters)
# {'ServerName': Parameter(name='Server Name', schema={...})}

# What does it return?
print(await file_system.sila_service.set_server_name.responses)
# None

Parameters come with names, data types, and constraints — units, min / max ranges, enumerations. Responses have the same shape. The SDK uses this schema to serialize your arguments correctly; the platform UI uses it to render a parameter form on Controls and a value view on Properties.