Get started with a device

Choose an SDK

Target

SDK directory

Use it for

Python 3

sdks/python/

reference integrations, Linux devices, and test automation

Android/Kotlin

sdks/android/

Android-based terminals and controllers

C11

sdks/fgai-mqtt-c/

embedded Linux, native controllers, and factory bootstrap software

Emulator

emulator/

repeatable virtual-device and failure scenarios

All SDKs use the same DDI-1 identity, HTTPS, MQTT, and OTA boundaries. Do not combine separate legacy device and Bazaar OTA clients. DDI is the sole terminal integration product; HawkBit compatibility is reached only through the DDI OTA prefix.

Provisioning origin and region

The platform serves two provisioning origins, one per region. A device is provisioned against the one its account’s region serves. The list and the transport port live in one place — Device connectivity — and are deliberately not repeated here, so there is no second copy to drift.

Each SDK names both as constants and supplies no default: fgai_mqtt.regions in Python, FGAI_ORIGIN_TOKYO and FGAI_ORIGIN_SEOUL in C, DdiOrigins in Android. Pass the origin where the device’s configuration is established — in Python, persist(..., origin=...) — so it is recorded beside the certificates and later OTA and certificate rotation read the same region back instead of deciding it again.

An origin is accepted in both of its forms and they are one origin: the host (things.flexgalaxy.ai) and the short name (tokyo, the form the platform itself records on the device). Passing one where the other was recorded is not a region change and is not refused.

Region is a property of the account: an operator binds it when the account is onboarded. It is not a per-device input, so no device, constructor, or provisioning request takes a region and nothing is homed per device. Choose the origin once per fleet, from the account’s region.

A device that is pointed at the other region’s origin is refused, and the refusal is terminal: the SDK surfaces a typed error and stops rather than backing off forever, and the recorded origin is left as it was. A configuration aimed at the other region’s origin is refused before the connection is attempted, so the device’s credential never reaches a broker that did not issue it. Moving a device between regions is an explicit re-provision, never a retry — see Errors and recovery for the per-SDK signal and the re-provision call.

Integration sequence

  1. Configure the DDI HTTPS origin this device is enrolled against. It is the origin of the account’s region: things.flexgalaxy.ai for Japan, things.ap-northeast-2.flexgalaxy.ai for Korea. The MQTT endpoint is not typed in: step 4 returns it as shared_config.mqtt_endpoint, and the SDK persists it beside the certificate and reads it back on reboot. Keep both configurable for customer-hosted deployments.

  2. Load the approved server trust anchors and the factory-issued bootstrap credential.

  3. Generate the production private key on the device, preferably in a TPM or secure element.

  4. Call POST /ddi/v1/identify, then POST /ddi/v1/provision with a CSR.

  5. Persist the returned leaf certificate and CA chain atomically. Delete the bootstrap credential after provisioning succeeds.

  6. Connect to MQTT 5 with mTLS, use the returned thing_id as the MQTT identity, and publish only to the documented device topics.

  7. Register inbound callbacks before connecting, then publish telemetry and the device-owned CLIENT attribute snapshot.

  8. Poll /ddi/v1/ota/ with the same device certificate when the product supports OTA.

Copy one of the repository’s minimal examples first, then adopt the robust example for production retry, persistence, and error handling. Never log the bootstrap bearer, enrollment token, private key, or raw authorization headers.

First operational session

The Python binding is the smallest executable reference. Install it from an authorized checkout:

python -m pip install ./sdks/python

After provisioning has persisted device.crt, device.key, ca.crt and the broker endpoint the provisioning response carried, load the returned identity and register inbound handlers before connect():

from fgai_mqtt import FGAIDeviceClient, ProvisionedIdentity
from fgai_mqtt._proto import command_pb2

# The SDK ships no default broker hostname: `load` reads back the endpoint
# persisted from the provisioning response's `shared_config.mqtt_endpoint`, and
# refuses when there is none rather than dialling an origin this device was
# never enrolled against.
identity = ProvisionedIdentity.load("certs")
device = FGAIDeviceClient(identity.device_config())

device.set_shared_attribute_callback(lambda key, value: apply_setting(key, value))
device.set_command_callback(
    lambda command, respond: respond(
        status=command_pb2.COMMAND_STATUS_SUCCEEDED,
        result=b"ok",
        error=None,
    )
)
device.connect()
device.publish_telemetry({"temperature": 22.5}, tags={"site": "factory-a"})
device.report_client_attribute({"firmware_version": "1.0.0"})

apply_setting is application code: apply and persist the requested shared attribute before returning. The SDK then acknowledges it. See Device operations for the Android/Kotlin and C forms and for the managed-runtime status of each operation.

Retry rules

  • Retry transport failures, 408, 429, and 5xx with exponential backoff and full jitter.

  • Honor Retry-After when present.

  • Do not retry other 4xx responses without correcting the request or identity.

  • Treat DDI-1-403-IDENTITY-BLACKLISTED as terminal.

  • Reconnect MQTT with backoff and republish the client-attribute snapshot after a successful reconnect.

Manufacturing boundary

The SDK provisioning flow is not a replacement for the factory bootstrap process. A bootstrap mini-OS may first read the hardware descriptor, enroll through DDI, and use the DDI OTA route to obtain an assigned artifact. See the repository’s docs/factory-bootstrap-flow.md for the complete sequence.