{
  "name": "HardwareBlocks",
  "version": "0.3.0",
  "status": "developer-beta",
  "description": "Four fixed XIAO kits, a private web app, authenticated device telemetry and app publication through REST and MCP.",
  "availability": {
    "docs": "live",
    "catalog_api": "live",
    "mcp": "public reads and private workspace tools",
    "device_telemetry": "HTTPS API live; firmware bench-test before dispatch",
    "pairing_app": "web claim live; Bluetooth commissioning planned",
    "hardware_kits": "order now; assembled and tested before dispatch"
  },
  "blocks": [
    {
      "id": "camera",
      "name": "Camera",
      "purpose": "Capture images for your application.",
      "status": "available in fixed kits",
      "connection": "Wi-Fi / HTTPS via XIAO ESP32S3"
    },
    {
      "id": "motion",
      "name": "Motion",
      "purpose": "Measure acceleration and rotation.",
      "status": "available in fixed kits",
      "connection": "Wi-Fi / HTTPS via XIAO ESP32S3"
    },
    {
      "id": "distance",
      "name": "Distance",
      "purpose": "Measure distance to an object for occupancy or proximity applications.",
      "status": "available in fixed kits",
      "connection": "Wi-Fi / HTTPS via XIAO ESP32S3"
    },
    {
      "id": "button",
      "name": "Button",
      "purpose": "Turn a physical press into an application event.",
      "status": "available in fixed kits",
      "connection": "Wi-Fi / HTTPS via XIAO ESP32S3"
    },
    {
      "id": "climate",
      "name": "Climate",
      "purpose": "Temperature and humidity in Climate + Touch Kit",
      "status": "available in fixed kit",
      "connection": "Wi-Fi / HTTPS"
    },
    {
      "id": "force",
      "name": "Force",
      "purpose": "Relative force/press input in Climate + Touch Kit",
      "status": "available in fixed kit",
      "connection": "Wi-Fi / HTTPS"
    }
  ],
  "recipes": [
    {
      "id": "fridge",
      "name": "Fridge cam",
      "file": "fridge.ts",
      "headline": "Your fridge has an API-shaped future.",
      "description": "A snapshot when the door closes. Your shopping app gets the update.",
      "blocks": [
        "camera"
      ],
      "additional_hardware": [
        "door-open/closed sensor",
        "controlled lighting",
        "battery or suitable power",
        "enclosure"
      ],
      "connection": "Wi-Fi → your application",
      "event": {
        "source": "simulation",
        "type": "fridge.snapshot",
        "device_id": "demo-fridge",
        "door": "closed",
        "image_url": null,
        "captured_at": "2026-10-29T12:00:00Z"
      },
      "result": "Snapshot event received. Add a real image when hardware is connected.",
      "trigger": "Close the fridge",
      "prompt": "Build a fridge dashboard with the latest snapshot, capture time and an editable shopping list. Start with the provided simulated event. Use a local placeholder image; image_url is null because no hardware is connected. Define a replaceable device adapter. Clearly show stale images and disconnected devices.",
      "requirements": "A closed fridge needs controlled lighting. Test condensation protection, mounting and Wi-Fi reception. A photo does not determine food freshness.",
      "pseudocode": [
        "on('fridge.snapshot', (event) => {",
        "  fridge.update(event);",
        "  shoppingList.show();",
        "});"
      ],
      "kit_id": "camera",
      "event_note": "Illustrative application-level event. Use /api/schema for the actual sensor ingestion contract."
    },
    {
      "id": "parking",
      "name": "Parking sensor",
      "file": "parking.ts",
      "headline": "Make a parking space queryable.",
      "description": "Distance becomes occupancy. Your app decides what happens next.",
      "blocks": [
        "distance"
      ],
      "additional_hardware": [
        "suitable power supply",
        "weather-appropriate enclosure",
        "fixed mount"
      ],
      "connection": "Wi-Fi → occupancy dashboard",
      "event": {
        "source": "simulation",
        "type": "parking.occupancy",
        "device_id": "demo-parking",
        "occupied": true,
        "distance_cm": 84,
        "observed_at": "2026-10-29T12:00:00Z"
      },
      "result": "Space occupied. The dashboard can update.",
      "trigger": "Park a car",
      "prompt": "Build a single-space parking dashboard using simulated distance events. Show available, occupied or unknown. Use a configurable calibrated threshold, require five seconds of stable readings before switching status, and show unknown when data is stale. Put device access behind a replaceable adapter.",
      "requirements": "Choose and calibrate the sensor for the installation. Account for weather and passing objects. This detects occupancy; it is not a collision-avoidance system.",
      "pseudocode": [
        "on('parking.occupancy', (event) => {",
        "  space.setStatus(event.occupied);",
        "  dashboard.refresh();",
        "});"
      ],
      "kit_id": "distance",
      "event_note": "Illustrative application-level event. Use /api/schema for the actual sensor ingestion contract."
    },
    {
      "id": "bike",
      "name": "Bike guard",
      "file": "bike.ts",
      "headline": "If it moves, your code knows.",
      "description": "A motion event from your bike. An alert defined by you.",
      "blocks": [
        "motion"
      ],
      "additional_hardware": [
        "battery",
        "protective enclosure",
        "bike mount",
        "nearby phone or gateway"
      ],
      "connection": "Wi-Fi / phone hotspot → application; Bluetooth relay planned",
      "event": {
        "source": "simulation",
        "type": "bike.motion",
        "device_id": "demo-bike",
        "armed": true,
        "motion_detected": true,
        "connection": "gateway-online",
        "observed_at": "2026-10-29T12:00:00Z"
      },
      "result": "Motion detected while armed. Your app can raise an alert.",
      "trigger": "Move the bike",
      "prompt": "Build a bike movement alarm with arm/disarm controls, adjustable sensitivity and visible connection status. Start with the provided simulated motion event and show alerts locally. Remote notifications require a connected phone or gateway with internet access. Separate a mock adapter from future hardware access; do not imply GPS tracking.",
      "requirements": "Launch Motion Kit sends through Wi-Fi (including a suitable phone hotspot). A native Bluetooth phone relay is not released. Remote alerts require internet connectivity. Test background behaviour and battery life. Movement alerts are not GPS tracking or a replacement for a lock.",
      "pseudocode": [
        "on('bike.motion', (event) => {",
        "  if (event.armed)",
        "    notify('Your bike just moved.');",
        "});"
      ],
      "kit_id": "motion",
      "event_note": "Illustrative application-level event. Use /api/schema for the actual sensor ingestion contract."
    },
    {
      "id": "ski",
      "name": "SkiTik",
      "file": "skitik.ts",
      "headline": "Put an event stream on your ski boot.",
      "description": "Acceleration and rotation become a session you can explore.",
      "blocks": [
        "motion"
      ],
      "additional_hardware": [
        "battery",
        "protective enclosure",
        "boot strap",
        "nearby phone"
      ],
      "connection": "Wi-Fi / phone hotspot → application; Bluetooth relay planned",
      "event": {
        "source": "simulation",
        "type": "ski.motion",
        "device_id": "demo-skitik",
        "acceleration_m_s2": {
          "x": 1.2,
          "y": 0.6,
          "z": 9.5
        },
        "gyroscope_deg_s": {
          "x": 2.4,
          "y": 18.2,
          "z": -3.1
        },
        "observed_at": "2026-10-29T12:00:00Z"
      },
      "result": "Motion sample received. Your session timeline gets a new point.",
      "trigger": "Take a turn",
      "prompt": "Build a ski-session explorer with a motion timeline and candidate turns from simulated accelerometer and gyroscope data. Separate raw measurements from estimates. Include calibration and connection status. Use a replaceable device adapter and do not claim measured accuracy or snow-tested performance.",
      "requirements": "Launch Motion Kit sends through Wi-Fi (including a suitable phone hotspot). A native Bluetooth phone relay is not released. SkiTik is an existing development project. Its CAD reference is a 50 × 32 mm low-profile USB-C pod. That is reference geometry, not a guaranteed HardwareBlocks specification. On-snow accuracy and cold-weather performance need validation.",
      "pseudocode": [
        "on('ski.motion', (sample) => {",
        "  session.append(sample);",
        "  timeline.render();",
        "});"
      ],
      "kit_id": "motion",
      "event_note": "Illustrative application-level event. Use /api/schema for the actual sensor ingestion contract."
    }
  ]
}