Examples
Example: IoT Monitoring
A real-time IoT monitoring module that subscribes to NATS telemetry events and displays live sensor data.
Example: IoT Monitoring Module
A real-time monitoring dashboard that subscribes to NATS telemetry events and displays live sensor data.
Live Telemetry Page
'use client';
import { useEffect, useState, useCallback } from 'react';
import { useShellAuth } from '@/lib/shell-auth';
import { useNats } from '@/hooks/useNats';
import { Thermometer, Droplets, Battery, Wifi, WifiOff } from 'lucide-react';
interface SensorReading {
device_id: string;
sensor_id: string;
temperature?: number;
humidity?: number;
battery?: number;
rssi?: number;
timestamp: string;
}
interface DeviceState {
[deviceId: string]: SensorReading;
}
export default function MonitoringPage() {
const { isAuthenticated } = useShellAuth();
const { connected, subscribe } = useNats();
const [devices, setDevices] = useState<DeviceState>({});
const [eventCount, setEventCount] = useState(0);
const handleTelemetry = useCallback((event: { data: unknown }) => {
const reading = event.data as SensorReading;
setDevices((prev) => ({
...prev,
[reading.device_id]: reading,
}));
setEventCount((prev) => prev + 1);
}, []);
useEffect(() => {
if (!connected) return;
const unsub = subscribe('telemetry', handleTelemetry);
return unsub;
}, [connected, subscribe, handleTelemetry]);
if (!isAuthenticated) {
return <p className="p-6 text-gray-400">Loading...</p>;
}
const deviceList = Object.values(devices);
return (
<div className="p-6 space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-white">IoT Monitoring</h1>
<p className="text-gray-400 mt-1">
Real-time sensor data via NATS
</p>
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-2">
{connected ? (
<Wifi className="h-4 w-4 text-green-400" />
) : (
<WifiOff className="h-4 w-4 text-red-400" />
)}
<span className="text-sm text-gray-400">
{connected ? 'Connected' : 'Disconnected'}
</span>
</div>
<span className="text-sm text-gray-500">
{eventCount} events received
</span>
</div>
</div>
{/* Device Grid */}
{deviceList.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{deviceList.map((device) => (
<div
key={device.device_id}
className="rounded-xl border border-gray-700 bg-gray-800/50 p-6"
>
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium text-white">
{device.device_id}
</h3>
<span className="text-xs text-gray-500">
{new Date(device.timestamp).toLocaleTimeString()}
</span>
</div>
<div className="grid grid-cols-2 gap-4">
{device.temperature !== undefined && (
<div className="flex items-center gap-2">
<Thermometer className="h-4 w-4 text-red-400" />
<div>
<p className="text-lg font-bold text-white">
{device.temperature.toFixed(1)}°C
</p>
<p className="text-xs text-gray-500">Temperature</p>
</div>
</div>
)}
{device.humidity !== undefined && (
<div className="flex items-center gap-2">
<Droplets className="h-4 w-4 text-blue-400" />
<div>
<p className="text-lg font-bold text-white">
{device.humidity.toFixed(1)}%
</p>
<p className="text-xs text-gray-500">Humidity</p>
</div>
</div>
)}
{device.battery !== undefined && (
<div className="flex items-center gap-2">
<Battery className="h-4 w-4 text-green-400" />
<div>
<p className="text-lg font-bold text-white">
{device.battery}%
</p>
<p className="text-xs text-gray-500">Battery</p>
</div>
</div>
)}
</div>
</div>
))}
</div>
) : (
<div className="rounded-xl border border-gray-700 bg-gray-800/50 p-12 text-center">
<Wifi className="h-12 w-12 text-gray-600 mx-auto mb-4" />
<h3 className="text-lg font-medium text-white mb-2">
Waiting for data...
</h3>
<p className="text-gray-400">
{connected
? 'Connected to NATS. Sensor data will appear here when devices send telemetry.'
: 'Connecting to WebSocket gateway...'}
</p>
</div>
)}
</div>
);
}This example demonstrates:
- Real-time NATS event subscription via
useNats() - Live device state management
- Connection status indicator
- Responsive card grid for device data
- Graceful empty state