DRAFT — Preliminary version, do not share. · Version préliminaire, ne pas partager.

Actions and custom encoders

Actions are your own operations on devices: you define how long a device has to receive and run them, how many times to retry and how urgent they are, and Fundamentum tracks each one device by device. With capabilities and encoders, one action works across device types and firmware versions that each expect a different format.

Actions flow: you create an action from an action type; with a capability, Fundamentum picks your encoder for each device type and firmware version, the encoded payload reaches the device over MQTT, and each device action moves from pending to success, failure, timeout or cancel

Before you begin

How it works

A device action goes through these statuses:

pending → queued → sent → waiting / ongoing → success | failure | timeout

A device action can also end as cancel, and retry marks one that's being sent again. success, failure, timeout and cancel are final. Each device action also records sent_at, received_at, started_at, done_at, its progression and its delivery and execution attempt counts. The action itself is scheduled, progressing or done, and its progression is the average over its devices.

1. Create an action type

fun api v1 action store-action-type --project-id <project-id> --body '{
  "project_id": <project-id>,
  "name": "Rotate logs",
  "type": 42,
  "delivering_timeout": 120,
  "execution_timeout": 300,
  "max_retries": 3,
  "priority": 1
}'
Field Meaning
delivering_timeout Seconds for the device to receive the action. Past it, the action is retried, then marked timeout
execution_timeout Seconds for the device to finish once it has started
max_retries How many times a failed or undelivered action is sent again (0 to 1000)
priority Lower runs first: priority 1 is sent before priority 10
capability_name Optional. Set it only when the action goes through an encoder (step 5)

Fundamentum also provides generic action types shared by every project. List them with fun api v1 action list-action-types --project-id <project-id>.

2. Send an action

payload is base64-encoded. devices lists device IDs:

fun api v1 action store-action --project-id <project-id> --body '{
  "action_type_id": <action-type-id>,
  "devices": [101, 102, 103],
  "description": "Weekly log rotation",
  "payload": "eyJrZWVwIjogN30="
}'

To schedule it, add "is_scheduled": true with activate_at, end_at or a cron expression. To target devices of one registry, use store-action-by-registry with --registry-id.

3. Track it end to end

fun api v1 action get-action --project-id <project-id> --action-id <action-id>
fun api v1 action get-device-actions --project-id <project-id> --device-id <device-id>
fun api v1 action get-device-actions-by-registry --project-id <project-id> --registry-id <registry-id>

In the Hub, the action's page and the device's Actions tab update live as devices report progress.

4. Cancel or retry

Actions can't be deleted, only cancelled. You act on all devices of an action, or on one:

fun api v1 action cancel-action --project-id <project-id> --action-id <action-id>
fun api v1 action retry-device-action --project-id <project-id> --device-id <device-id> --action-id <action-id>

5. Use one action across device types: capabilities and encoders

  1. Create a capability, the name of what devices can do. Names are 1 to 64 characters: letters, digits, ., - and _, starting with a letter or digit.

    fun api v1 capability store-capability --project-id <project-id> \
      --body '{"name": "device.restart", "description": "Restart the device"}'
    
  2. Pick a protocol. It defines the device topic the encoded payload is published on. Protocols are platform-wide:

    fun api v1 capability get-encoder-protocols --project-id <project-id>
    
  3. Deploy your encoder function (see Cloud functions), then register it as an encoder:

    fun api v1 capability store-encoder --project-id <project-id> \
      --body '{"name": "Model B restart encoder", "protocol_id": <protocol-id>, "function_id": "<function-id>", "parameters": {}}'
    
  4. Link the capability to the encoder for a device type, and optionally a firmware version. A link for a specific version_id wins over a link for all versions:

    fun api v1 capability store-capability-encoder --project-id <project-id> --capability-id <capability-id> \
      --body '{"encoder_id": <encoder-id>, "device_type_id": <device-type-id>, "version_id": <version-id>}'
    
  5. Create an action type with "capability_name": "device.restart" and send actions as in step 2.

Writing an encoder

Your encoder is called in two modes:

The status, message and progress of the device action always come from the device's response. decode only translates its payload; if it fails, the status still applies and the device action records the decoding error.

The call carries four metadata values: Mode (encode or decode), Capability, Action-Id and Device-Ids (comma-separated). On a groupable protocol, one call can cover several devices that share the same encoder. The function writes one JSON document to stdout, in both modes:

{"status": "success", "data_type": "json", "data": {"command": "restart", "delay_s": 5}}

Use "data_type": "binary" with base64 data for binary payloads, or {"status": "error", "error": "..."} to fail. See Cloud functions: runtime and SDK for reading input and metadata.

What's next