Use when adding or updating a hardware module in this repo and you need the full path from MQTT ingress to Supabase persistence, agent tool design, and validation. Covers how a new block is recognized, how data enters hardware_events and hardware_history, when to define generic versus device-specific tools, and how to verify the end-to-end query path for agent answers.
Use this skill when integrating a new hardware block into the current Bun + Hono + MQTT + Supabase stack.
This skill is for the current repo architecture, not the older mock or PC-host designs.
After following this skill, a new hardware module should satisfy all of these:
HardwareStorehardware_eventshardware_historyThere are two storage layers. Do not mix them up.
hardware_events: raw event log from MQTT ingresshardware_history: block-level historical snapshots derived from HardwareStoreUse this rule:
hardware_eventshardware_historyRead only these files first:
src/main/hardware/mqtt-bridge.tssrc/main/hardware/mqtt-protocol.tssrc/main/hardware/store.tssrc/main/history/hardware-event-service.tssrc/main/history/supabase-history-service.tssrc/main/tools/hardware-events/index.tssrc/main/tools/hardware-history/index.tssrc/main/tools/device-tools/index.tssrc/main/agent.tsBefore changing code, write down:
node_id format, for example env_hello01node_type, for example env, cam, ledaihub/sensor/{node_id}/datasensor_data, online, offline, cmdIf the device does not fit the current AI Hub envelope:
msg_idnode_idpayloadtstypevthen fix the device-side or bridge-side contract first. Do not patch downstream tools until ingress shape is stable.
The server must be able to infer:
node_typecapabilityblock type = sensor / stream / actuatorUpdate:
src/main/hardware/mqtt-protocol.tssrc/main/hardware/mqtt-bridge.tsTypical work:
AihubNodeType if needednode_type -> capabilitynode_type -> block typenode_type -> chip familyIf the device sends camera- or actuator-specific payloads, extend the extraction logic in mqtt-bridge.ts.
hardware_eventsRaw event persistence should happen before any higher-level interpretation.
Current path:
MQTT -> AihubMqttBridge.handleMessage -> HardwareEventService.insertMqttEnvelope -> Supabase hardware_events
Check:
parseAihubTopic(...)normalizeAihubMqttEnvelope(...)payloadcapability, scope, subject, type, msg_id, and recorded_at are correctIf the new hardware introduces new semantics such as person detection, gesture detection, occupancy count, or ASR transcript events, prefer storing that directly in payload and query it later from hardware_events.
HardwareStoreRaw events are not enough. The block also needs a usable current state.
Current path:
MQTT -> AihubMqttBridge.toIngressMessages -> HardwareStore.applyMessage
Check which HardwareIngressMessage variants the device should produce:
announcestatustelemetrysnapshotactuator_statecommand_resultRules:
telemetrysnapshot or telemetry with clearly queryable fieldsactuator_state and command_resultIf the device is input-only and you want the agent to read the latest state directly, make sure HardwareStore.getSensorData(...) or HardwareStore.getCameraScene(...) can serve it.
hardware_historyCurrent path:
HardwareStore update -> hardware.subscribe(...) -> SupabaseHistoryService.persistSnapshot(...) -> Supabase hardware_history
Important repo-specific rules:
hardware_history stores block snapshots, not raw MQTT envelopesrecorded_at should reflect each block's last_seen_msOnly data present in the snapshot payload is persisted, so verify the block contributes meaningful latest, scene, or actuator data.
If a device's important information exists only as transient events and should not be flattened into a block snapshot, query it from hardware_events instead of forcing it into hardware_history.
There are three tool layers in this repo.
Use existing tools when the device fits standard behavior:
list_blocksget_sensor_dataget_camera_snapshotget_hardware_historyget_hardware_eventsChoose this path when:
block_idUse src/main/tools/device-tools/index.ts when a specific physical device should be directly callable by name.
Use this when:
For each dedicated device tool define:
blockIdlabeldescriptionDo not create device-specific tools for every sensor by default. Add them when direct naming materially improves reliability.
If the new hardware answers event questions such as:
then do not rely on the model to count raw rows freehand.
Create a specialized tool on top of hardware_events for that device or event type.
Examples:
count_people_detected_last_minutesget_presence_eventsget_latest_asr_transcriptWhen adding a new tool, encode the table choice into the description.
Good pattern:
Do not expose table names to end users. Expose semantics.
Keep these boundaries:
hardware_historyhardware_eventsRun these in order after integrating a new hardware block.
bun run typecheck:node
bun test
Start the server and inspect:
GET /readyGET /v1/blocksGET /v1/hardware-eventsGET /v1/historyConfirm:
/v1/blockshardware_events contains raw MQTT rows for the blockhardware_history contains snapshot rows for the block if the block contributes snapshot-worthy stateUse the existing scripts when applicable:
bun run mqtt:test:railwaybun run hardware-events:test:writeFor a real new device, publish one known-good message and verify:
hardware_eventsHardwareStorehardware_historyhardware_eventstelemetry in HardwareStoreget_sensor_dataget_hardware_history for trendshardware_eventshardware_historyhardware_eventsHardwareStorehardware_historyblock_idhardware_events was persistedA hardware integration is not done until all are true:
list_blockshardware_events