code wiki / (root) / nx_iot_anchor.nx

nx_iot_anchor.nx

buildroot/runtime/nx_iot_anchor.nx

19460 B486 linesdepth 0pulls 0 transitivereach 11 importersview sourcekind librarytopic iot
docsdependenciesstructsconstsfunctions

about

nx_iot_anchor.nx -- stable device-identity anchor for the hub. Roadmap: NISHI_IOT_HUB_ROADMAP.md Epoch 3 (PAIR) keystone + the cross-cutting identity substrate every later epoch writes into. THE PROBLEM THIS CLOSES (operator-reported, 2026-05-16 + 2026-06-18): "they disconnect all the time and blink and then I manually have to repair and then they duplicate in the app ... light bulbs aren't wandering electronics, they should stay anchored like sprinklers." Incumbent apps (Smart Life / Kasa / etc.) key a device by a CLOUD-ISSUED registration token. When a fixture drops WiFi, reverts to pairing mode, and is re-paired, the cloud mints a NEW token -> a NEW device row appears, the old one goes stale, and scenes/automations silently point at the dead row. The fixture never moved; its IDENTITY wandered. That is the duplication bug. THE FIX (structural, not heuristic -- per feedback-honest-perf-verdict-no-aspirational-claims): Anchor identity to the device's STABLE HARDWARE id (its MAC, or a vendor gwId) -- NOT to an IP, a session, or a cloud token. A (re)discovery of a known hardware id RE-ADOPTS the existing logical device: same logical_id, same friendly name, same history; only the volatile fields (ip, last_seen, reachability) update. A physical device therefore maps to EXACTLY ONE logical record for its entire life, no matter how many times it blinks, drops, and re-pairs. Duplication is eliminated BY CONSTRUCTION, not by a de-dup pass that can be skipped. HONEST SCOPE (what this primitive does NOT do): - It does not, by itself, stop a fixture from dropping WiFi. That is the LAN-session + watchdog + provision layers (queued: nx_iot_watchdog.nx, nx_iot_provision_softap.nx). - It does not talk to the network. It is pure-buffer: the caller (discovery / provision / daemon) hands it a hardware id + the volatile facts, and it returns the stable logical identity. This is the IDENTITY spine those layers hang on. VENDOR-AGNOSTIC BY DESIGN (per feedback-hub-primitive-thin-per-site-

dependencies 0 imports · 3 importers

nx_iot_anchor.nx nx_iot_anchor_test.nx nx_iot_classify.nx nx_iot_discover.nx

imports: none

imported by: nx_iot_anchor_test.nxnx_iot_classify.nxnx_iot_discover.nx

structs

160struct IotAnchorReg
173struct IotDeviceMeta

consts

111const NX_IOT_VENDOR_UNKNOWN: i64 = 0
112const NX_IOT_VENDOR_TUYA: i64 = 1
113const NX_IOT_VENDOR_MAGICHOME: i64 = 2
114const NX_IOT_VENDOR_WIZ: i64 = 3
115const NX_IOT_VENDOR_YEELIGHT: i64 = 4
116const NX_IOT_VENDOR_KASA: i64 = 5
117const NX_IOT_VENDOR_N: i64 = 6
127const NX_IOT_ANCHOR_UNKNOWN: i64 = 0
128const NX_IOT_ANCHOR_NEW: i64 = 1 // first sighting -> new logical device
129const NX_IOT_ANCHOR_READOPTED: i64 = 2 // known hwid -> SAME logical_id (no dup)
130const NX_IOT_ANCHOR_UPDATED: i64 = 3 // metadata-only change (name / reachability)
131const NX_IOT_ANCHOR_FULL: i64 = 4 // registry at capacity
132const NX_IOT_ANCHOR_BAD_ARG: i64 = 5
133const NX_IOT_ANCHOR_NOT_FOUND: i64 = 6
134const NX_IOT_ANCHOR_N: i64 = 7
153const NX_IOT_HWID_CAP: i64 = 32 // fits a 17-char MAC string or a gwId
154const NX_IOT_NAME_CAP: i64 = 32 // friendly name, e.g. "Kitchen Switch"
155const NX_IOT_BLOB_STRIDE: i64 = 64 // HWID_CAP + NAME_CAP
156const NX_IOT_DEV_META_BYTES: i64 = 64 // 8 i64 fields

functions

119func nx_iot_vendor_is_valid(v: i64) -> i64
136func nx_iot_anchor_verdict_is_valid(v: i64) -> i64
called by 1: main
186func nx_iot_anchor_meta_ptr(metas: *IotDeviceMeta, slot: i64) -> *IotDeviceMeta
191func nx_iot_anchor_hwid_base(slot: i64) -> i64
195func nx_iot_anchor_name_base(slot: i64) -> i64
201func nx_iot_anchor_store_hwid(blob: *u8, slot: i64,
215func nx_iot_anchor_hwid_eq(blob: *u8, slot: i64,
240func nx_iot_anchor_find(reg: *IotAnchorReg, blob: *u8,
254func nx_iot_anchor_slot_by_logical(reg: *IotAnchorReg, metas: *IotDeviceMeta,
272func nx_iot_anchor_init(reg: *IotAnchorReg, cap: i64) -> i64
called by 3: mainmainmain
294func nx_iot_anchor_adopt(reg: *IotAnchorReg, metas: *IotDeviceMeta,
367func nx_iot_anchor_set_name(reg: *IotAnchorReg, metas: *IotDeviceMeta,
409func nx_iot_anchor_mark_unreachable(reg: *IotAnchorReg, metas: *IotDeviceMeta,
425func nx_iot_anchor_count(reg: *IotAnchorReg) -> i64
called by 3: mainmainmain
430func nx_iot_anchor_naive_adopts(reg: *IotAnchorReg) -> i64
called by 3: mainmainmain
437func nx_iot_anchor_duplicates_prevented(reg: *IotAnchorReg) -> i64
442func nx_iot_anchor_count_current(reg: *IotAnchorReg, metas: *IotDeviceMeta) -> i64
called by 1: main calls 1: nx_iot_anchor_meta_ptr
456func nx_iot_anchor_logical_adopt_count(reg: *IotAnchorReg, metas: *IotDeviceMeta,
464func nx_iot_anchor_logical_is_current(reg: *IotAnchorReg, metas: *IotDeviceMeta,
472func nx_iot_anchor_logical_vendor(reg: *IotAnchorReg, metas: *IotDeviceMeta,
480func nx_iot_anchor_logical_ip(reg: *IotAnchorReg, metas: *IotDeviceMeta,