-
Notifications
You must be signed in to change notification settings - Fork 0
Variables
简体中文 · Home · Networking
This chapter defines the behavior shared by the SDKs. Language-specific return types and scheduling rules are documented in each SDK reference.
In this chapter
- 1. Names and references
- 2. Values, absence and initial loading
- 3. Reading, writing and observing
- 4. Concurrent writes and deletion
- 5. Offline operation and lifetime
- 6. Desired state and commands
A variable is identified by namespace and variable name. Both must match on communicating devices. The same name in another namespace is a different variable.
const hub = new KinopioHub('workshop');
const battery = hub.var('battery');Keep a reference and reuse it throughout the application. Creating it does not assign a value.
| Naming rule | Requirement |
|---|---|
| Length | 1–128 well-formed UTF-8 bytes |
| Excluded characters | U+0000–U+001F and U+007F |
| Comparison | Case-sensitive; no trimming or Unicode normalization |
| Slash | Literal content; /battery is a valid ROS variable name |
Sharing requires an explicit namespace. Omission creates a new UUID for each Hub, so two default Hubs are isolated.
| Runtime | Read the namespace |
|---|---|
| JS / Python | hub.namespace |
| C++ / ESP32 | hub.namespaceName() |
The namespace stays unchanged for the Hub lifetime. Creating a variable reference never starts a new namespace.
State names are encoded byte by byte as two lowercase hexadecimal digits. Spaces, dots, * and > remain literal in set/get/watch operations. Message methods have separate hierarchy and wildcard rules. For namespace workshop and variable battery:
| Traffic | NATS subject |
|---|---|
| Variable update | 776f726b73686f70.62617474657279 |
| Peer query | _sys.v4.776f726b73686f70.sync |
| Query reply | _sys.v4.776f726b73686f70.inbox.<id> |
| SDK report | _sys.v4.776f726b73686f70.health.<instanceId> |
| Python live | _sys.v4.776f726b73686f70.live.… |
Updates carry {name, version: {counter, writer}, deleted, value?}.
- The record keeps the original name; receivers check it against the encoded subject.
- Encoded data subjects cannot collide with control subjects.
- NATS permissions must cover data, queries, replies and health reports.
The shared encoding vectors list accepted and rejected names.
Namespaces organize data, not permissions. Devices also need a connected NATS topology and compatible authentication. Do not use a namespace as a substitute for NATS account or subject permissions.
| Data | Portable representation |
|---|---|
| JSON | Null, booleans, finite numbers, strings, arrays and objects |
| Integer-valued numbers | Within ±(2^53−1), even when the language supports larger integers |
| Larger identifiers | Strings |
| Dates and binary | Explicit application-defined JSON representation |
SDKs validate nesting, complexity and memory limits. A value accepted by a desktop SDK may still exceed the ESP32's smaller limits. Plan the shared schema around the smallest receiving device.
| State | JS | Python | C++ | ESP32 |
|---|---|---|---|---|
| Present JSON null | null |
None |
Optional containing Json(nullptr)
|
exists() is true; JSON is null |
| No local value | undefined |
UNSET |
Empty optional |
exists() is false |
| Initial lookup unfinished | meta.exists === null |
meta["exists"] is None |
meta()["exists"] is null |
No equivalent tri-state metadata API |
| Runtime | Initial lookup |
|---|---|
| JS / Python / C++ |
variable.ready() waits for a known local state, including known absence; check existence afterward. |
| ESP32 | Observe through loop() and callbacks; there is no variable ready() API. |
Ready does not mean present. It also does not mean every possible peer has answered.
Do not implement initialization as an assumed atomic “read absent, then set.” Two devices can both see absence and write defaults. The normal conflict rule chooses the winner; there is no compare-and-set operation.
Reads return local copies. Keep the variable reference; call set() to publish a changed value.
ESP32 ownership: Stored snapshots own nested ArduinoJson strings, including strings linked to caller arrays. Reads return an owned JsonDocument. See value ownership.
const battery = hub.var('battery');
const stop = battery.watch((value, meta) => {
if (meta.exists) console.log(value);
});
await battery.set(80);
// Call stop() when this view is no longer needed.Watches expose initial state and subsequent changes according to the language's scheduling rules. Metadata changes, such as pending publication clearing or connection changes, can also notify a watcher. Treat it as a current-state view, not a durable event stream or a guarantee of exactly one callback per business action.
| Method | Behavior |
|---|---|
get(fallback) |
Local missing-value default; does not replace null/false/zero or start a network lookup |
watchValue(handler) / Python watch_value
|
Value-only callback with the same watch timing |
pub/sub/req/handle |
Independent message operations; deleting state does not remove subscriptions |
Writes update local RAM. A successful write can occur while offline. Repeated writes of equal JSON still create new logical versions; transport deduplication only removes repetitions of the same version.
| Wait or operation | What success means |
|---|---|
Hub ready() (JS/Python/C++) |
Local SDK initialization completed |
Variable ready()
|
This local view has an initialization result |
Hub connected() (JS/Python/C++) |
The SDK has an active NATS connection |
set() / delete |
The local current record was updated |
Hub flush()
|
Current records were sent and NATS transport was confirmed |
| Application result variable | Whatever completion rule the application explicitly implements |
Each record carries {counter, writer}.
- Compare
counternumerically; it is encoded as a decimal string. - Higher counter wins; writer ID order breaks ties.
- Receiving a record advances the local logical clock before later writes.
Wall-clock timestamps do not choose the winner.
Merging selects a whole JSON value, not individual properties.
| Data relationship | Model it as |
|---|---|
| Fields with independent writers | Separate variables |
| Fields that travel as one snapshot | One object; accept whole-value conflict resolution |
| After deletion | Result |
|---|---|
| RAM record | A versioned tombstone remains |
| Older peer value arrives | Cannot restore the deleted value |
| Later higher-version write | Can create the value again |
| Capacity | Tombstones still consume record slots; deletion does not reclaim all metadata |
There is no atomic operation spanning several variables. A reader can observe an intermediate state between two writes. Put inseparable fields in one value or define an application-level sequence identifier.
- A new Hub starts with empty memory and a new identity.
- Online peers exchange their current records.
- During a disconnect, a living SDK can keep reading and updating local RAM.
- Reconnection merges retained current records with online peers.
- Exiting the last process holding a record loses that record.
- Only the current record is kept. Several offline changes can collapse before publication; there is no operation log.
- A broker is not a backup. Restarting it never restores SDK data, and leaving it online does not retain values.
- Living peers repair state. Periodic synchronization repairs missed updates while copies remain alive.
Use variables for current measurements, configuration and desired state. A device should publish a separate reported value after applying a request. Include an application request ID when the requester needs to associate a result with an action.
Use Python live channels for expiring commands when supported by the receiver. Live sends are separate calls with no offline replay; they still do not prove execution. ROS controls add session checks for desired state and receiver leases for live commands.
Use events and requests for transient notifications and application replies. Do not use a current value as an event log or atomic counter. Neither current values nor Core NATS queue groups guarantee that every intermediate operation is processed.
Next: connection modes and mesh, then the API reference for JS, Python, C++ or ESP32.
Home · 简体中文 · Edit the docs · 文档维护
Start here: JavaScript quick start · 快速上手
- JavaScript: Start / API · 中文
- Python: Start / API · 中文
- C++: Start / API · 中文
- ESP32: Start / API · 中文
- ROS 2: Start / Config · 中文