Skip to content

Demo-Ansicht (Capability Lab)

Damit sich alle Capability-Renderer der App ohne ein echtes, verbundenes Gerät ansehen lassen, gibt es ein lokales Demo-Thing: das Capability Lab. Es ist kein echtes BLE- oder MQTT-Gerät, sondern eine statisch definierte Thing-Model-Datei, die von der App genauso behandelt wird wie ein reguläres, registriertes Thing.

graph LR
    JSON["capability-lab.json"] --> Demo["demoDevice.ts"]
    Demo --> Thing["DEMO_THING<br/>(Thing)"]
    Demo --> Record["DEMO_DEVICE_RECORD<br/>(DeviceRecord)"]
    Thing --> UI["Capability-Renderer<br/>in der App"]
    Record --> UI

Erkennung des Demo-Geräts

Jede UUID, die mit demo- beginnt (oder exakt der festen Demo-ID entspricht), wird von der App als Demo-Gerät erkannt und über DEMO_THING / DEMO_DEVICE_RECORD bedient statt über REST/BLE/MQTT.

export const DEMO_DEVICE_ID = 'demo-capability-lab-01';

export function isDemoId(uuid: string): boolean {
  return uuid === DEMO_DEVICE_ID || uuid.startsWith('demo-');
}

Thing Model des Demo-Geräts

Das Capability Lab deckt alle unterstützten Capability-Typen und Display-Varianten in einem einzigen Thing ab: Toggle, Slider, Graph, Gauge, Progress, Indicator, Buttons (Enum), Impulse-Button, Dual-Joystick-Controller, Text-Input, Color-Picker, Time-Picker und Message.

{
  "success": true,
  "data": {
    "uuid": "demo-capability-lab-01",
    "device_id": "demo",
    "mac": "",
    "source": "demo",
    "connection_status": "online",
    "last_seen": "2026-06-01T12:00:00.000Z",
    "thing_model": {
      "device_id": "demo-capability-lab-01",
      "mac": "",
      "metadata": {
        "label": "Capability Demo",
        "category": "Demo",
        "description": "Local demo device that shows every supported capability renderer"
      },
      "capabilities": [
        { "id": "power", "type": "ON_OFF", "label": "Power Toggle", "display": "toggle", "direction": "both" },
        { "id": "brightness", "type": "NUMBER", "label": "Brightness", "direction": "both", "display": "slider", "min": 0, "max": 255, "step": 1, "unit": "PWM" },
        { "id": "temperature", "type": "NUMBER", "label": "Temperature", "direction": "readable", "display": "graph", "min": -20, "max": 80, "step": 0.1, "unit": "C" },
        { "id": "humidity", "type": "NUMBER", "label": "Luftfeuchte", "direction": "readable", "display": "gauge", "min": 0, "max": 100, "step": 1, "unit": "%" },
        { "id": "water_tank", "type": "NUMBER", "label": "Wassertank", "direction": "both", "display": "progress", "min": 0, "max": 100, "step": 1, "unit": "%" },
        { "id": "battery", "type": "NUMBER", "label": "Akku", "direction": "readable", "display": "progress", "min": 0, "max": 100, "step": 1, "unit": "%" },
        { "id": "pump_status", "type": "ON_OFF", "label": "Pumpe", "direction": "readable", "display": "indicator" },
        {
          "id": "mode",
          "type": "ENUM",
          "label": "Operating Mode",
          "direction": "both",
          "display": "buttons",
          "values": [
            { "id": "auto", "label": "Auto" },
            { "id": "boost", "label": "Boost" },
            { "id": "eco", "label": "Eco" }
          ]
        },
        { "id": "calibrate", "type": "IMPULSE", "label": "Calibration Pulse", "direction": "writable", "display": "button" },
        { "id": "controller", "type": "CONTROLLER", "label": "Dual Joystick Controller", "direction": "both", "display": "dual_joystick" },
        { "id": "device_note", "type": "TEXT", "label": "Device Note", "direction": "both", "display": "input" },
        { "id": "led_color", "type": "TEXT", "label": "LED Farbe", "direction": "both", "display": "color-picker" },
        { "id": "wake_time", "type": "TEXT", "label": "Weckzeit", "direction": "both", "display": "time-picker" },
        { "id": "status_message", "type": "MESSAGE", "label": "Status Message", "direction": "readable", "display": "text" }
      ],
      "state": {
        "power": false,
        "brightness": 64,
        "temperature": 22.4,
        "humidity": 58,
        "water_tank": 72,
        "battery": 41,
        "pump_status": true,
        "mode": "auto",
        "controller": { "joystick1": { "x": 0, "y": 0 }, "joystick2": { "x": 0, "y": 0 } },
        "device_note": "Demo ready",
        "led_color": "#33aaff",
        "wake_time": "07:30",
        "status_message": "All capability renderers are visible."
      }
    }
  }
}

Aufbereitung als Thing / DeviceRecord

Die App liest die JSON-Datei einmalig ein und wandelt sie in ein Thing- sowie ein DeviceRecord-Objekt um. Beide teilen dieselben Capabilities und Metadaten, damit sich das Demo-Gerät in jeder Ansicht der App identisch verhält wie ein echtes Gerät. Ein fixes last_seen-Datum sorgt dafür, dass die Demo-Ansicht immer denselben, reproduzierbaren Stand zeigt.

import type { Capability, Thing, ThingState, StateValue } from '../types';
import type { DeviceRecord } from '../RestAPI';
import demoJson from '../../assets/demo/capability-lab.json';

export const DEMO_DEVICE_ID = 'demo-capability-lab-01';

export function isDemoId(uuid: string): boolean {
  return uuid === DEMO_DEVICE_ID || uuid.startsWith('demo-');
}

const raw = demoJson.data;
const FIXED_DATE = '2026-06-01T12:00:00.000Z';

const thingState: ThingState = Object.fromEntries(
  Object.entries(raw.thing_model.state).map(([key, value]) => [
    key,
    { value: value as StateValue, last_update: FIXED_DATE },
  ]),
);

export const DEMO_THING: Thing = {
  uuid: raw.uuid,
  thing_id: raw.uuid,
  device_id: raw.uuid,
  mac: raw.mac,
  metadata: { label: raw.thing_model.metadata.label, category: raw.thing_model.metadata.category },
  capabilities: raw.thing_model.capabilities as unknown as Capability[],
  state: thingState,
  connection: { status: 'ONLINE', last_seen: FIXED_DATE },
};

export const DEMO_DEVICE_RECORD: DeviceRecord = {
  uuid: raw.uuid,
  device_id: raw.device_id,
  mac: raw.mac,
  last_seen: FIXED_DATE,
  connection_status: 'ONLINE',
  thing_model: {
    metadata: { label: raw.thing_model.metadata.label, category: raw.thing_model.metadata.category },
    capabilities: raw.thing_model.capabilities as unknown as Capability[],
  },
};

Nutzen für Entwicklung und Vorführung

Da das Capability Lab jede unterstützte Display-Variante in einem einzigen Thing kombiniert, dient es als:

  • Schnelltest beim Entwickeln neuer Capability-Renderer, ohne dass Hardware angeschlossen sein muss
  • Referenz dafür, welche type/display-Kombinationen im Thing Model gültig sind
  • Vorführ-Gerät für Demos, da es ohne WLAN, MQTT-Broker oder ESP32 sofort funktioniert

Siehe auch