Device attributes

Attributes are key/value facts attached to a thing — firmware_version, polling_interval_seconds, fleet_tier, and so on. Every attribute lives in exactly one of three buckets that differ in who may write it and whether it syncs to the device. As a device integrator you work with two of the three.

The three buckets

Bucket

Who writes

Device sync

Example keys

CLIENT

the device only

device reports up; cloud reads

firmware_version, mac_address, boot_count, hardware_revision

SHARED

the cloud AND the device

both directions

polling_interval_seconds, telemetry_sample_rate, enabled_features

SERVER

the cloud only

never sent to the device

tenant-side organization only

Your device firmware touches CLIENT (facts it reports) and SHARED (config it receives and acknowledges). The SERVER bucket is cloud-side organization and never reaches the device — you can ignore it.

Keys are yours to define. A key must match ^[a-zA-Z_][a-zA-Z0-9_]{0,62}$; a value can be a string, number, boolean, small object, or array.

CLIENT — report facts up

The device is the source of truth for CLIENT attributes. It reports them on connect and whenever they change; the cloud can read them but never writes them. CLIENT reporting rides the device’s existing reported-state channel — there is no separate wire message to construct.

If two reports for the same key arrive out of order, the platform keeps the one with the later device timestamp (last-writer-wins by device time), so a delayed or replayed report never overwrites a newer value.

SHARED — receive config, acknowledge it

SHARED attributes are configuration both sides care about. The cloud pushes a new value to the device; the device effects it and acknowledges it back. The platform records the acknowledged value as the device’s current effective setting.

If the device is offline when the cloud writes a SHARED value, the write is queued and delivered on the device’s next connection — you don’t lose a config change made while a device was asleep.

Wire topics

CLIENT and SHARED use the following topics on your device’s own subtree (see Device MQTT topic access):

Topic

Direction

Carries

device/{thing_id}/attribute/client

device → cloud

reported CLIENT attributes (reported-state wire)

device/{thing_id}/attribute/shared/push

cloud → device

a SHARED value to effect

device/{thing_id}/attribute/shared/ack

device → cloud

acknowledgment of the effected value

The frozen wire messages (DeviceAttributeSharedPush, DeviceAttributeSharedAck) are defined in the DDI-1 contract. Use the SDK below rather than encoding them by hand.

Python SDK

The device-side DDI SDK (SiriusVoyager/ddi, Python) handles both directions for you. Construct a client, register a SHARED callback, and report CLIENT attributes:

from fgai_mqtt import FGAIDeviceClient, DeviceConfig

client = FGAIDeviceClient(DeviceConfig(device_id="my-thing-id", ...))

# Receive SHARED-bucket pushes from the cloud. The SDK effects the value into a
# local shadow and publishes the ack for you — your callback just applies it.
def apply_shared(key, value):
    print(f"cloud set {key} = {value}")

client.set_shared_attribute_callback(apply_shared)
client.connect()

# Report CLIENT-bucket facts up to the cloud.
client.report_client_attribute({"firmware_version": "8.4.2", "boot_count": 12})

# The last SHARED values the device received + effected:
print(client.shared_attribute_shadow)

Call

Bucket

What it does

report_client_attribute(attributes, version=None)

CLIENT

report device-owned facts up

set_shared_attribute_callback(fn)

SHARED

register fn(key, value), invoked to effect each cloud push before the SDK auto-acks

shared_attribute_shadow

SHARED

the device’s last-effected SHARED values (key value)

The SDK always publishes the SHARED acknowledgment after your callback runs, so the cloud’s record of the effective value stays in step with the device — even if your callback raises.