Device operations¶
This guide covers the complete unified DDI device capability set, including
Bazaar-authored OTA delivery. All operations use the provisioned
thing_id and certificate; none requires a device application to know an
internal service hostname or token.
Check Runtime surface before relying on an operation. A binding can implement a protocol message before the managed platform consumes that message end to end.
Provision and rotate identity¶
The common flow is identify → generate a key and CSR locally → provision → persist → connect. Only the CSR crosses the network. The private key must remain in a hardware-backed or otherwise protected device store.
Python:
from fgai_mqtt import ProvisionedIdentity, ProvisioningClient
from fgai_mqtt._csr import build_csr
provisioner = ProvisioningClient(
"https://things.flexgalaxy.ai",
attestation_bearer=bootstrap_bearer,
)
identity = provisioner.identify(model="RB-100", sn="SN-0001")
csr_pem, key_pem = build_csr("SN-0001", "RB-100", identity.thing_id)
issued = provisioner.provision(
identity.thing_id,
identity.enrollment_token,
csr_pem,
)
device_identity = ProvisionedIdentity.persist(issued, key_pem, "certs")
Android/Kotlin uses DdiDeviceSdk.provisionDevice, which composes identify,
on-device CSR generation, and provision on Dispatchers.IO.
DdiDeviceSdk.buildMqttConfig performs the MQTT hand-off. Persist the returned
DdiDeviceCredentials in a protected application/device store and call
bindCredentials after reboot.
C uses fgai_provisioning_client_new, fgai_provisioning_identify,
fgai_provisioning_build_csr, and fgai_provisioning_provision. Free every
result with its matching fgai_provisioning_*_free function.
For rotation, generate a fresh key and CSR, configure the current certificate
for mTLS, then call the binding’s rotate operation. Persist the new certificate
and key as one atomic update. The rotate response contains only thing_id and
certificate; retain the existing CA chain and MQTT configuration.
Publish telemetry¶
Telemetry is a map of numeric metrics plus optional string tags. Bindings encode
DeviceTelemetry, add the device identity and current time, and publish QoS 0 to
device/<thing_id>/telemetry. The managed route is live.
device.publish_telemetry(
{"temperature": 22.5, "battery_percent": 87.0},
tags={"site": "factory-a"},
)
client.publishTelemetry(
metrics = mapOf("temperature" to 22.5, "battery_percent" to 87.0),
tags = mapOf("site" to "factory-a"),
)
fgai_mqtt_metric_t metrics[] = {
{"temperature", 22.5},
{"battery_percent", 87.0},
};
fgai_mqtt_telemetry_t sample = FGAI_MQTT_TELEMETRY_INIT;
sample.metrics = metrics;
sample.n_metrics = 2;
fgai_mqtt_client_publish_telemetry(client, &sample);
QoS 0 favors freshness over replay. Add an application sequence when duplicate or missing sample detection matters; the SDK auto-increments when omitted.
For a tablet, controller, and MCU that form one customer-visible product, follow How to integrate a composite robot. It shows which identity publishes each producer’s telemetry and why the MCU does not need a third broker connection.
Report device-owned attributes¶
Use the CLIENT attribute path for firmware, hardware, and current device state.
It is live and bridged to the platform through Kafka. The Python name is
report_client_attribute; its wire shape is the same reported-state protobuf
used by publish_reported_state.
device.report_client_attribute({
"firmware_version": "1.0.0",
"hardware_revision": "rev-b",
})
client.publishReportedState(
mapOf("firmware_version" to "1.0.0", "hardware_revision" to "rev-b")
)
fgai_mqtt_metric_t state[] = {{"battery_percent", 87.0}};
fgai_mqtt_client_publish_reported(client, state, 1, reported_version++);
The C convenience API currently accepts flat numeric values only. Use Python or
Android when the state requires strings, booleans, or nested protobuf Struct
values.
Republish a complete CLIENT snapshot after reconnect so the platform can recover from retained-session loss.
Receive and answer commands¶
Register the callback before connect. A command handler must be idempotent by
command_id, perform bounded work, and preserve the supplied response function
or correlation data.
from fgai_mqtt._proto import command_pb2
def on_command(command, respond):
result = execute_once(command.command_id, command.name, command.payload)
respond(
status=command_pb2.COMMAND_STATUS_SUCCEEDED,
result=result,
error=None,
)
device.set_command_callback(on_command)
Android uses FgaiMqttClient.setCommandCallback { command, respond -> ... }.
C registers fgai_mqtt_client_set_command_callback and answers with
fgai_mqtt_client_respond_command, passing the request correlation bytes.
Command delivery to devices is live. Managed persistence of
device/<thing_id>/commands/ack is reserved, so a backend must not yet treat the
MQTT response as a durable audit record.
Heartbeat and lifecycle¶
All three MQTT bindings can publish heartbeat and lifecycle protobufs. Connect
and graceful disconnect automatically emit lifecycle events, and an MQTT Will
represents unexpected disconnect. Python and Android expose heartbeat helpers;
C exposes fgai_mqtt_client_publish_heartbeat and
fgai_mqtt_client_publish_lifecycle.
The managed platform currently observes broker presence, but does not persist the heartbeat or lifecycle topics as backend events. Use these messages for forward compatibility, not as the only health or decommissioning signal.
Poll and install OTA¶
OTA stays within the same certificate boundary:
Poll
https://things.flexgalaxy.ai/ddi/v1/otawith device mTLS.Follow the returned HawkBit HAL relations through the SDK only.
Send download/execution feedback to the provided feedback relation.
Download to a temporary destination with an expected SHA-256 value.
Publish the artifact to its final location only after integrity verification.
Install with the product’s A/B, recovery, or transactional update mechanism.
Report final execution and result feedback.
Python:
root = device.ota.poll()
action = root.link("deploymentBase")
if action:
deployment = device.ota.get_document(action)
artifact_href = deployment.find_links("download-http")[0]
artifact = device.ota.download(
artifact_href,
"/var/lib/device/update.bin",
expected_sha256=expected_sha256,
)
Android obtains DdiDeviceSdk.ota after provisioning or binding restored
credentials, then calls poll, document, feedback, and download. C creates
fgai_ota_client_t, then uses fgai_ota_poll,
fgai_ota_get, fgai_ota_feedback, and fgai_ota_download.
Every binding rejects a HAL link that changes origin or escapes
/ddi/v1/ota. Devices never receive or send a HawkBit GatewayToken, controller
ID, or tenant. Bazaar authors assignments and rollout policy; the private
shared-service HawkBit deployment executes them behind DDI.
Desired-state compatibility¶
Python exposes set_twin_callback; Android exposes setTwinCallback; C exposes
fgai_mqtt_client_set_twin_delta_callback. These names predate the frozen
attribute wire contract. On the managed platform, cloud-owned configuration is
the SHARED push flow described above. Prefer shared-attribute APIs for new code.
See SDK reference for exact method signatures and Errors and recovery for failure handling.