Skip to main content

Device Type Development

This page covers NeoMind's device data model, data ingestion protocols (MQTT / Webhook), the auto-discovery flow, and CLI device management. By the end you can connect any sensor or actuator to NeoMind.

Repo: device type definitions go to camthink-ai/NeoMind-DeviceTypes. Runtime device management lives in the main repo's neomind-devices crate.

Device Data Model

DeviceConfig (Device Instance)

Every device connected to NeoMind is a DeviceConfig instance stored in data/device_registry.redb:

pub struct DeviceConfig {
pub device_id: String, // globally unique id (e.g. "living-room-sensor")
pub name: String, // display name
pub device_type: String, // references DeviceTypeTemplate (e.g. "dht22_sensor")
pub adapter_type: String, // adapter: mqtt / webhook / hass
pub connection_config: ConnectionConfig, // protocol-specific config
pub adapter_id: Option<String>, // adapter managing this device
pub last_seen: i64, // last active timestamp (0 = never connected)
}

ConnectionConfig (Connection Config)

The connection config is protocol-agnostic, supporting multiple protocols via Option fields:

pub struct ConnectionConfig {
// MQTT-specific
pub telemetry_topic: Option<String>, // uplink data topic
pub command_topic: Option<String>, // downlink command topic
pub json_path: Option<String>, // JSON extraction path

// Home Assistant-specific
pub entity_id: Option<String>,

// Other protocol-specific params (flattened extension)
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}

DeviceTypeTemplate (Device Type Template)

A device type template defines the metrics and commands for a class of devices. Multiple device instances can reference the same template:

pub struct DeviceTypeTemplate {
pub device_type: String, // unique id (e.g. "dht22_sensor")
pub name: String, // display name
pub description: String,
pub categories: Vec<String>, // grouping (e.g. ["sensor", "environment"])
pub mode: DeviceTypeMode, // Simple or Full
pub metrics: Vec<MetricDefinition>, // metrics provided
pub uplink_samples: Vec<Value>, // sample data for Simple mode
pub commands: Vec<CommandDefinition>, // supported commands
}

MetricDefinition (Metric Definition)

pub struct MetricDefinition {
pub name: String, // metric name (supports dot notation, e.g. "values.temperature")
pub display_name: String, // display name
pub data_type: MetricDataType, // Float / Integer / Boolean / String / Binary / Enum
pub unit: String, // unit (e.g. "°C", "%", "hPa")
pub min: Option<f64>, // range (optional)
pub max: Option<f64>,
}

The DataSourceId format is device:{device_id}:{metric_name}. Dashboards and the rule engine reference device metrics by this ID.

Data Ingestion Protocols

NeoMind embeds an MQTT 3.1.1 broker (port 1883). Devices connect directly.

Topic format:

DirectionTopicDescription
Uplink (telemetry)device/{device_type}/{device_id}/uplinkDevice sends data
Downlink (command)device/{device_type}/{device_id}/downlinkNeoMind sends commands

Payload format (JSON):

{
"temperature": 23.5,
"humidity": 65.2,
"battery": 3.7
}

If the device payload is nested, set json_path in ConnectionConfig to specify the extraction path (e.g. data.values).

Webhook

For devices with only HTTP capability (e.g. ESP32-CAM, Raspberry Pi).

Endpoint: POST http://<server_ip>:9375/api/devices/{device_id}/webhook

Payload:

{
"timestamp": 1718300000,
"quality": 1.0,
"data": {
"temperature": 25.3,
"humidity": 60
}
}

timestamp can be omitted (auto-filled with current time). The data object keys must match the device type's metric names.

Auto-Discovery Flow

Auto-discovery is enabled by default. Any unknown device sending data to the MQTT broker is captured as a Draft, pending user approval.

Device publishes to any MQTT topic


Matches a known device type? ─── yes ──→ Device created (auto-registered)
│ no

Draft created (collects 5 samples)


User approves via Web UI or CLI ─── approve ──→ Registered as a device
│ │
│ reject │ Metrics written to telemetry.redb
▼ │
Draft deleted ▼
Dashboard / Rules / Agent ready

CLI draft management:

# List pending devices
neomind device drafts list

# View details (with sample data)
neomind device drafts get <DRAFT_ID>

# Approve (specify name and type)
neomind device drafts approve <DRAFT_ID> --name "Living Room Sensor" --type dht22_sensor

# Reject
neomind device drafts reject <DRAFT_ID>

CLI Device Management

Creating Devices

# MQTT device
neomind device create \
--name "Temp/Humidity Sensor" \
--device-type dht22_sensor \
--adapter-type mqtt \
--json '{"connection_config": {"telemetry_topic": "device/dht22/living-room/uplink"}}'

# Webhook device
neomind device create \
--name "Webhook Device" \
--adapter-type webhook \
--json '{"connection_config": {}}'

# Get webhook URL
neomind device webhook-url <DEVICE_ID>
# → http://192.168.1.100:9375/api/devices/abc123/webhook

Querying and Controlling

# List devices
neomind device list [--device-type <TYPE>] [--status <STATUS>]

# View device details + current metric values
neomind device get <DEVICE_ID>

# View telemetry history
neomind device history <DEVICE_ID> [--metric temperature] [--time-range 1h]

# Send command
neomind device control <DEVICE_ID> set_brightness \
--params '{"brightness": 80}'

# Write metric directly (for testing)
neomind device write-metric <DEVICE_ID> temperature 25.5

Device Type Management

# List all device types
neomind device types list

# Create device type
neomind device types create \
--name 'Temperature Sensor' \
--metrics '[{"name":"temperature","display_name":"Temperature","data_type":"Float","unit":"°C"}]' \
--commands '[{"name":"calibrate","display_name":"Calibrate","description":"Calibrate sensor","parameters":[{"name":"offset","data_type":"Float"}]}]'

# View type details
neomind device types get <TYPE_ID>

REST API Quick Reference

MethodPathDescription
GET/api/devicesList devices
POST/api/devicesCreate device
GET/api/devices/{id}Device details
PUT/api/devices/{id}Update device
DELETE/api/devices/{id}Delete device
GET/api/devices/{id}/currentLatest metric values
POST/api/devices/{id}/command/{cmd}Send command
POST/api/devices/{id}/webhookWebhook data ingestion
GET/api/devices/draftsPending device drafts
POST/api/devices/drafts/{id}/approveApprove draft
GET/api/device-typesList device types

Full API reference: REST API — Devices

Hands-On Example: ESP32 + MQTT

Here is a complete example of an ESP32 + DHT22 temperature/humidity sensor connecting to NeoMind via MQTT.

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

#define DHT_PIN 4
#define DHT_TYPE DHT22

const char* wifi_ssid = "YourWiFi";
const char* wifi_pass = "password";
const char* mqtt_host = "192.168.1.100"; // NeoMind server IP
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHT_PIN, DHT_TYPE);

void setup() {
Serial.begin(115200);
dht.begin();

WiFi.begin(wifi_ssid, wifi_pass);
while (WiFi.status() != WL_CONNECTED) delay(500);

client.setServer(mqtt_host, mqtt_port);
}

void loop() {
if (!client.connected()) {
while (!client.connect("esp32-dht22")) delay(1000);
}

float temp = dht.readTemperature();
float hum = dht.readHumidity();

if (!isnan(temp) && !isnan(hum)) {
char payload[64];
snprintf(payload, 64,
"{\"temperature\":%.1f,\"humidity\":%.1f}", temp, hum);
client.publish("device/dht22/living-room/uplink", payload);
}

client.loop();
delay(5000); // report every 5 seconds
}

After the device powers on, NeoMind's auto-discovery captures it. Approve it in the CLI:

neomind device drafts list
neomind device drafts approve <DRAFT_ID> --name "Living Room Sensor" --type dht22_sensor

Hands-On Example: Python + Webhook

For scenarios without an MQTT library:

import requests
import time
import random

DEVICE_ID = "webhook-sensor-01"
API_BASE = "http://192.168.1.100:9375/api"
WEBHOOK_URL = f"{API_BASE}/devices/{DEVICE_ID}/webhook"

while True:
payload = {
"data": {
"temperature": round(20 + random.random() * 10, 1),
"humidity": round(40 + random.random() * 30, 1),
}
}
resp = requests.post(WEBHOOK_URL, json=payload)
print(f"Status: {resp.status_code}")
time.sleep(10)

Troubleshooting

ProblemCauseSolution
Device data not ingestedMQTT topic mismatchCheck telemetry_topic config
Auto-discovery not triggeringAuto-discovery disabledneomind device drafts config --enabled true
Webhook returns 404device_id doesn't existCreate the device first with neomind device create
Metric values nullpayload key ≠ metric nameVerify MetricDefinition.name matches payload JSON keys
Command not receivedDevice not subscribed to downlinkCheck device code subscribes to device/{type}/{id}/downlink

Next Steps


Last updated: 2026-06-15