Prerequisites
- Basic understanding of JavaScript ๐
- TypeScript installation โก
- VS Code or preferred IDE ๐ป
What you'll learn
- Understand the concept fundamentals ๐ฏ
- Apply the concept in real projects ๐๏ธ
- Debug common issues ๐
- Write type-safe code โจ
๐ฏ Introduction
Welcome to this exciting tutorial on building an IoT Dashboard with MQTT integration in TypeScript! ๐ In this guide, weโll explore how to create a real-time IoT monitoring system that can handle sensor data, device control, and live updates.
Youโll discover how MQTT (Message Queuing Telemetry Transport) can transform your IoT applications with its lightweight, publish-subscribe messaging protocol. Whether youโre monitoring smart home devices ๐ , industrial sensors ๐ญ, or agricultural systems ๐พ, understanding MQTT integration is essential for building scalable IoT solutions.
By the end of this tutorial, youโll have a fully functional IoT dashboard that can connect to MQTT brokers and visualize real-time data! Letโs dive in! ๐โโ๏ธ
๐ Understanding MQTT and IoT Dashboards
๐ค What is MQTT?
MQTT is like a super-efficient postal service for IoT devices ๐ฎ. Think of it as WhatsApp for machines - devices can send messages to topics, and any device subscribed to those topics receives the messages instantly!
In TypeScript terms, MQTT provides a lightweight messaging protocol perfect for IoT devices with limited resources. This means you can:
- โจ Send and receive messages with minimal bandwidth
- ๐ Handle thousands of connected devices
- ๐ก๏ธ Ensure reliable message delivery
- ๐ Build real-time dashboards
๐ก Why Use MQTT for IoT Dashboards?
Hereโs why developers love MQTT for IoT applications:
- Lightweight Protocol ๐ชถ: Minimal overhead, perfect for constrained devices
- Real-time Updates โก: Instant message delivery for live data
- Scalability ๐: Handle millions of messages efficiently
- Quality of Service ๐ฏ: Guaranteed message delivery options
- Bi-directional Communication ๐: Send commands and receive data
Real-world example: Imagine monitoring a smart greenhouse ๐ฑ. Temperature sensors publish data to โgreenhouse/temperatureโ, humidity sensors to โgreenhouse/humidityโ, and your dashboard subscribes to these topics for real-time monitoring!
๐ง Basic Syntax and Usage
๐ Setting Up MQTT Client
Letโs start with a friendly example:
// ๐ Hello, MQTT!
import mqtt from 'mqtt';
// ๐จ Define our connection options
interface MqttOptions {
host: string; // ๐ Broker address
port: number; // ๐ช Connection port
clientId: string; // ๐ Unique client identifier
username?: string; // ๐ค Optional authentication
password?: string; // ๐ Optional password
}
// ๐ Create MQTT client
const createMqttClient = (options: MqttOptions): mqtt.MqttClient => {
const url = `mqtt://${options.host}:${options.port}`;
const client = mqtt.connect(url, {
clientId: options.clientId,
username: options.username,
password: options.password,
clean: true, // ๐งน Start with clean session
reconnectPeriod: 1000 // ๐ Auto-reconnect every second
});
console.log(`๐ Connecting to MQTT broker at ${url}...`);
return client;
};
๐ก Explanation: Weโre creating a type-safe MQTT client with automatic reconnection. The emojis in comments make the code more readable and fun!
๐ฏ Common MQTT Patterns
Here are patterns youโll use daily:
// ๐๏ธ Pattern 1: Device data structure
interface SensorData {
deviceId: string; // ๐ Unique device identifier
type: 'temperature' | 'humidity' | 'motion'; // ๐ Sensor type
value: number; // ๐ Measured value
unit: string; // ๐ Measurement unit
timestamp: Date; // ๐ When measured
location: string; // ๐ Device location
}
// ๐จ Pattern 2: Topic structure
type TopicPattern = `devices/${string}/${string}`;
type CommandTopic = `devices/${string}/commands`;
type DataTopic = `devices/${string}/data`;
// ๐ Pattern 3: Message handler with types
const handleMessage = (topic: string, payload: Buffer): void => {
try {
const data: SensorData = JSON.parse(payload.toString());
console.log(`๐จ Received from ${topic}:`, data);
} catch (error) {
console.error(`โ Failed to parse message:`, error);
}
};
๐ก Practical Examples
๐ Example 1: Smart Home Dashboard
Letโs build something real:
// ๐ Smart Home IoT Dashboard
interface SmartDevice {
id: string;
name: string;
type: 'light' | 'thermostat' | 'sensor' | 'camera';
status: 'online' | 'offline';
emoji: string;
data?: any;
}
class SmartHomeDashboard {
private client: mqtt.MqttClient;
private devices: Map<string, SmartDevice> = new Map();
constructor(private brokerUrl: string) {
// ๐ Connect to MQTT broker
this.client = mqtt.connect(brokerUrl, {
clientId: `dashboard_${Date.now()}`
});
this.setupEventHandlers();
}
// ๐ฏ Set up MQTT event handlers
private setupEventHandlers(): void {
this.client.on('connect', () => {
console.log('โ
Connected to MQTT broker!');
this.subscribeToTopics();
});
this.client.on('message', (topic, payload) => {
this.handleDeviceMessage(topic, payload);
});
this.client.on('error', (error) => {
console.error('โ MQTT Error:', error);
});
}
// ๐ก Subscribe to device topics
private subscribeToTopics(): void {
const topics = [
'home/+/status', // ๐ Device status updates
'home/+/data', // ๐ Sensor data
'home/+/alerts' // ๐จ Alert messages
];
this.client.subscribe(topics, (err) => {
if (!err) {
console.log('๐ก Subscribed to topics:', topics);
}
});
}
// ๐จ Handle incoming messages
private handleDeviceMessage(topic: string, payload: Buffer): void {
const [, deviceId, messageType] = topic.split('/');
const data = JSON.parse(payload.toString());
switch (messageType) {
case 'status':
this.updateDeviceStatus(deviceId, data);
break;
case 'data':
this.updateDeviceData(deviceId, data);
break;
case 'alerts':
this.handleAlert(deviceId, data);
break;
}
}
// ๐ก Control smart light
controlLight(deviceId: string, state: boolean, brightness?: number): void {
const command = {
action: 'set',
state: state ? 'on' : 'off',
brightness: brightness || 100,
timestamp: new Date()
};
this.client.publish(
`home/${deviceId}/commands`,
JSON.stringify(command),
{ qos: 1 } // ๐ฌ Ensure delivery
);
console.log(`๐ก Light ${deviceId}: ${command.state} at ${command.brightness}%`);
}
// ๐ก๏ธ Set thermostat
setThermostat(deviceId: string, temperature: number): void {
const command = {
action: 'set_temperature',
value: temperature,
unit: 'celsius',
timestamp: new Date()
};
this.client.publish(
`home/${deviceId}/commands`,
JSON.stringify(command),
{ qos: 2 } // ๐ฏ Exactly once delivery
);
console.log(`๐ก๏ธ Thermostat ${deviceId} set to ${temperature}ยฐC`);
}
// ๐ Get dashboard stats
getDashboardStats(): void {
const online = Array.from(this.devices.values())
.filter(d => d.status === 'online').length;
console.log('๐ Dashboard Stats:');
console.log(` ๐ Total devices: ${this.devices.size}`);
console.log(` โ
Online: ${online}`);
console.log(` โ Offline: ${this.devices.size - online}`);
}
// ๐ Update device status
private updateDeviceStatus(deviceId: string, status: any): void {
const device = this.devices.get(deviceId);
if (device) {
device.status = status.online ? 'online' : 'offline';
console.log(`${device.emoji} ${device.name} is ${device.status}`);
}
}
// ๐ Update device data
private updateDeviceData(deviceId: string, data: any): void {
const device = this.devices.get(deviceId);
if (device) {
device.data = data;
this.visualizeData(device);
}
}
// ๐จ Visualize device data
private visualizeData(device: SmartDevice): void {
switch (device.type) {
case 'sensor':
console.log(`๐ ${device.name}: ${device.data.value}${device.data.unit}`);
break;
case 'thermostat':
console.log(`๐ก๏ธ ${device.name}: ${device.data.current}ยฐC โ ${device.data.target}ยฐC`);
break;
case 'light':
console.log(`๐ก ${device.name}: ${device.data.state} (${device.data.brightness}%)`);
break;
}
}
// ๐จ Handle alerts
private handleAlert(deviceId: string, alert: any): void {
const device = this.devices.get(deviceId);
if (device) {
console.log(`๐จ ALERT from ${device.name}: ${alert.message}`);
// Here you could trigger notifications, emails, etc.
}
}
}
// ๐ฎ Let's use it!
const dashboard = new SmartHomeDashboard('mqtt://localhost:1883');
// Add some devices
dashboard['devices'].set('light_001', {
id: 'light_001',
name: 'Living Room Light',
type: 'light',
status: 'online',
emoji: '๐ก'
});
dashboard['devices'].set('thermo_001', {
id: 'thermo_001',
name: 'Main Thermostat',
type: 'thermostat',
status: 'online',
emoji: '๐ก๏ธ'
});
๐ฏ Try it yourself: Add motion sensors and create automation rules!
๐ญ Example 2: Industrial IoT Monitoring
Letโs make it industrial:
// ๐ญ Industrial IoT Monitoring System
interface IndustrialSensor {
id: string;
type: 'temperature' | 'pressure' | 'vibration' | 'flow';
location: string;
thresholds: {
min: number;
max: number;
critical: number;
};
}
interface SensorReading {
sensorId: string;
value: number;
timestamp: Date;
status: 'normal' | 'warning' | 'critical';
}
class IndustrialIoTDashboard {
private client: mqtt.MqttClient;
private sensors: Map<string, IndustrialSensor> = new Map();
private readings: Map<string, SensorReading[]> = new Map();
private alertCallbacks: ((alert: any) => void)[] = [];
constructor(brokerConfig: MqttOptions) {
this.client = createMqttClient(brokerConfig);
this.initialize();
}
// ๐ Initialize dashboard
private initialize(): void {
this.client.on('connect', () => {
console.log('๐ญ Industrial IoT Dashboard Connected!');
this.subscribeToFactoryTopics();
});
this.client.on('message', (topic, payload) => {
this.processSensorData(topic, payload);
});
}
// ๐ก Subscribe to factory sensors
private subscribeToFactoryTopics(): void {
const topics = [
'factory/+/sensors/+/data', // ๐ Sensor readings
'factory/+/alerts', // ๐จ System alerts
'factory/+/maintenance', // ๐ง Maintenance updates
'factory/+/production' // ๐ Production metrics
];
this.client.subscribe(topics, { qos: 1 });
}
// ๐ Process sensor data
private processSensorData(topic: string, payload: Buffer): void {
const topicParts = topic.split('/');
const data = JSON.parse(payload.toString());
if (topic.includes('/sensors/') && topic.endsWith('/data')) {
this.handleSensorReading(topicParts[3], data);
} else if (topic.includes('/alerts')) {
this.handleSystemAlert(data);
}
}
// ๐ Handle sensor reading
private handleSensorReading(sensorId: string, data: any): void {
const sensor = this.sensors.get(sensorId);
if (!sensor) return;
const reading: SensorReading = {
sensorId,
value: data.value,
timestamp: new Date(data.timestamp),
status: this.evaluateReading(data.value, sensor.thresholds)
};
// ๐ Store reading
if (!this.readings.has(sensorId)) {
this.readings.set(sensorId, []);
}
this.readings.get(sensorId)!.push(reading);
// ๐จ Check for alerts
if (reading.status !== 'normal') {
this.triggerAlert(sensor, reading);
}
// ๐ Log reading
const emoji = this.getSensorEmoji(sensor.type);
console.log(`${emoji} ${sensor.location}: ${reading.value} - ${reading.status}`);
}
// ๐ฏ Evaluate reading status
private evaluateReading(value: number, thresholds: any): SensorReading['status'] {
if (value >= thresholds.critical || value <= thresholds.min) {
return 'critical';
} else if (value >= thresholds.max * 0.9) {
return 'warning';
}
return 'normal';
}
// ๐จ Trigger alert
private triggerAlert(sensor: IndustrialSensor, reading: SensorReading): void {
const alert = {
type: reading.status,
sensor: sensor,
reading: reading,
message: `${sensor.type} in ${sensor.location} is ${reading.status}!`,
timestamp: new Date()
};
// ๐ข Publish alert
this.client.publish(
'factory/dashboard/alerts',
JSON.stringify(alert),
{ qos: 2, retain: true }
);
// ๐ Notify callbacks
this.alertCallbacks.forEach(callback => callback(alert));
console.log(`๐จ ALERT: ${alert.message}`);
}
// ๐ Get sensor statistics
getSensorStats(sensorId: string): any {
const readings = this.readings.get(sensorId) || [];
if (readings.length === 0) return null;
const values = readings.map(r => r.value);
const avg = values.reduce((a, b) => a + b, 0) / values.length;
const max = Math.max(...values);
const min = Math.min(...values);
return {
average: avg.toFixed(2),
max: max,
min: min,
readings: readings.length,
lastReading: readings[readings.length - 1]
};
}
// ๐จ Get sensor emoji
private getSensorEmoji(type: string): string {
const emojis: Record<string, string> = {
temperature: '๐ก๏ธ',
pressure: '๐ฏ',
vibration: '๐ณ',
flow: '๐'
};
return emojis[type] || '๐';
}
// ๐ง Send maintenance command
scheduleMaintenance(equipmentId: string, date: Date): void {
const command = {
action: 'schedule_maintenance',
equipmentId,
scheduledDate: date,
type: 'preventive'
};
this.client.publish(
`factory/maintenance/schedule`,
JSON.stringify(command),
{ qos: 2 }
);
console.log(`๐ง Maintenance scheduled for ${equipmentId} on ${date.toLocaleDateString()}`);
}
// ๐ Get production metrics
getProductionMetrics(): void {
this.client.publish('factory/production/request', JSON.stringify({
type: 'daily_summary',
date: new Date()
}));
}
}
๐ Advanced Concepts
๐งโโ๏ธ Advanced Topic 1: Real-time Data Visualization
When youโre ready to level up, try this advanced pattern:
// ๐ฏ Real-time data stream processor
type DataPoint = {
value: number;
timestamp: number;
deviceId: string;
};
class RealTimeDataProcessor<T extends DataPoint> {
private dataBuffer: Map<string, T[]> = new Map();
private windowSize: number = 100; // ๐ Keep last 100 points
private updateCallbacks: Map<string, (data: T[]) => void> = new Map();
// โจ Process incoming data point
processDataPoint(point: T): void {
const buffer = this.dataBuffer.get(point.deviceId) || [];
buffer.push(point);
// ๐ Maintain window size
if (buffer.length > this.windowSize) {
buffer.shift();
}
this.dataBuffer.set(point.deviceId, buffer);
// ๐ข Notify subscribers
const callback = this.updateCallbacks.get(point.deviceId);
if (callback) {
callback(buffer);
}
}
// ๐ Calculate moving average
getMovingAverage(deviceId: string, window: number = 10): number | null {
const buffer = this.dataBuffer.get(deviceId);
if (!buffer || buffer.length < window) return null;
const recent = buffer.slice(-window);
const sum = recent.reduce((acc, point) => acc + point.value, 0);
return sum / window;
}
// ๐ Detect anomalies
detectAnomalies(deviceId: string, threshold: number = 2): T[] {
const buffer = this.dataBuffer.get(deviceId);
if (!buffer || buffer.length < 10) return [];
const avg = this.getMovingAverage(deviceId, 20) || 0;
const stdDev = this.calculateStdDev(buffer.map(p => p.value));
return buffer.filter(point =>
Math.abs(point.value - avg) > threshold * stdDev
);
}
// ๐ Calculate standard deviation
private calculateStdDev(values: number[]): number {
const avg = values.reduce((a, b) => a + b, 0) / values.length;
const squaredDiffs = values.map(v => Math.pow(v - avg, 2));
const variance = squaredDiffs.reduce((a, b) => a + b, 0) / values.length;
return Math.sqrt(variance);
}
}
๐๏ธ Advanced Topic 2: MQTT Security & Authentication
For the brave developers:
// ๐ Secure MQTT connection with certificates
interface SecureMqttConfig extends MqttOptions {
ca?: Buffer; // ๐๏ธ Certificate Authority
cert?: Buffer; // ๐ Client certificate
key?: Buffer; // ๐ Private key
rejectUnauthorized?: boolean; // ๐ก๏ธ Verify server cert
}
class SecureIoTDashboard {
private client: mqtt.MqttClient;
private encryptionKey: string;
constructor(config: SecureMqttConfig) {
// ๐ Create secure connection
const secureOptions = {
...config,
protocol: 'mqtts', // ๐ Use TLS
ca: config.ca,
cert: config.cert,
key: config.key,
rejectUnauthorized: config.rejectUnauthorized ?? true
};
this.client = mqtt.connect(`mqtts://${config.host}:${config.port}`, secureOptions);
this.encryptionKey = this.generateEncryptionKey();
}
// ๐ Encrypt sensitive data
private encryptPayload(data: any): string {
// In production, use proper encryption library
const encrypted = Buffer.from(JSON.stringify(data)).toString('base64');
return encrypted;
}
// ๐ Decrypt received data
private decryptPayload(encrypted: string): any {
const decrypted = Buffer.from(encrypted, 'base64').toString();
return JSON.parse(decrypted);
}
// ๐ Generate encryption key
private generateEncryptionKey(): string {
return Math.random().toString(36).substring(7);
}
// ๐ก๏ธ Publish with encryption
publishSecure(topic: string, data: any): void {
const encrypted = this.encryptPayload(data);
this.client.publish(topic, encrypted, {
qos: 2,
retain: false
});
}
}
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Memory Leaks with Subscriptions
// โ Wrong way - memory leak!
class LeakyDashboard {
subscribeToDevice(deviceId: string): void {
this.client.on('message', (topic, payload) => {
if (topic.includes(deviceId)) {
// This handler never gets removed! ๐ฅ
console.log(payload.toString());
}
});
}
}
// โ
Correct way - proper cleanup!
class CleanDashboard {
private messageHandlers: Map<string, (topic: string, payload: Buffer) => void> = new Map();
subscribeToDevice(deviceId: string): void {
const handler = (topic: string, payload: Buffer) => {
if (topic.includes(deviceId)) {
console.log(`๐จ ${deviceId}:`, payload.toString());
}
};
this.messageHandlers.set(deviceId, handler);
this.client.on('message', handler);
}
unsubscribeFromDevice(deviceId: string): void {
const handler = this.messageHandlers.get(deviceId);
if (handler) {
this.client.removeListener('message', handler);
this.messageHandlers.delete(deviceId);
console.log(`๐ Unsubscribed from ${deviceId}`);
}
}
}
๐คฏ Pitfall 2: Handling Connection Loss
// โ Dangerous - no reconnection handling!
const client = mqtt.connect('mqtt://broker.example.com');
client.subscribe('sensors/+/data'); // What if connection drops? ๐ฅ
// โ
Safe - proper reconnection!
class ResilientDashboard {
private subscriptions: string[] = [];
private isConnected: boolean = false;
constructor() {
this.setupClient();
}
private setupClient(): void {
this.client = mqtt.connect('mqtt://broker.example.com', {
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
clean: false, // ๐ Persist session
clientId: `dashboard_${Date.now()}`
});
this.client.on('connect', () => {
console.log('โ
Connected to MQTT broker');
this.isConnected = true;
this.resubscribe(); // ๐ Restore subscriptions
});
this.client.on('offline', () => {
console.log('โ ๏ธ MQTT client offline');
this.isConnected = false;
});
this.client.on('error', (error) => {
console.error('โ MQTT error:', error);
});
}
subscribe(topic: string): void {
if (!this.subscriptions.includes(topic)) {
this.subscriptions.push(topic);
}
if (this.isConnected) {
this.client.subscribe(topic, { qos: 1 });
}
}
private resubscribe(): void {
if (this.subscriptions.length > 0) {
this.client.subscribe(this.subscriptions, { qos: 1 });
console.log('๐ Resubscribed to topics:', this.subscriptions);
}
}
}
๐ ๏ธ Best Practices
- ๐ฏ Use Specific Topics: Donโt subscribe to โ#โ - be precise with your topic patterns!
- ๐ Type Your Messages: Always define interfaces for your MQTT payloads
- ๐ก๏ธ Handle Errors Gracefully: Network issues will happen - be prepared
- ๐จ Use QoS Wisely: QoS 0 for non-critical, QoS 1 for important, QoS 2 for critical
- โจ Clean Up Resources: Remove event listeners and unsubscribe when done
- ๐ Secure Your Connections: Always use TLS in production
- ๐ Monitor Performance: Track message rates and connection health
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Smart City IoT Dashboard
Create a comprehensive smart city monitoring system:
๐ Requirements:
- โ Monitor traffic sensors, air quality, and parking spots
- ๐ท๏ธ Support multiple zones (downtown, suburbs, industrial)
- ๐ค Real-time alerts for threshold violations
- ๐ Historical data tracking and trends
- ๐จ Each sensor type needs its own visualization!
๐ Bonus Points:
- Add predictive analytics for traffic flow
- Implement emergency response system integration
- Create a mobile-friendly dashboard view
- Add weather data correlation
๐ก Solution
๐ Click to see solution
// ๐๏ธ Smart City IoT Dashboard Solution!
interface CityZone {
id: string;
name: string;
type: 'downtown' | 'residential' | 'industrial' | 'commercial';
emoji: string;
}
interface CitySensor {
id: string;
type: 'traffic' | 'air_quality' | 'parking' | 'noise';
zoneId: string;
location: { lat: number; lng: number };
status: 'active' | 'maintenance' | 'offline';
}
interface SensorMetrics {
traffic: { flow: number; congestion: number; incidents: number };
air_quality: { pm25: number; pm10: number; co2: number; aqi: number };
parking: { occupied: number; total: number; turnover: number };
noise: { level: number; peak: number; average: number };
}
class SmartCityDashboard {
private client: mqtt.MqttClient;
private zones: Map<string, CityZone> = new Map();
private sensors: Map<string, CitySensor> = new Map();
private metrics: Map<string, SensorMetrics[keyof SensorMetrics]> = new Map();
private alerts: any[] = [];
constructor(brokerUrl: string) {
this.client = mqtt.connect(brokerUrl, {
clientId: `smart_city_dashboard_${Date.now()}`,
clean: false
});
this.initializeZones();
this.setupEventHandlers();
}
// ๐๏ธ Initialize city zones
private initializeZones(): void {
const zones: CityZone[] = [
{ id: 'downtown', name: 'Downtown', type: 'downtown', emoji: '๐๏ธ' },
{ id: 'suburbs', name: 'Suburbs', type: 'residential', emoji: '๐๏ธ' },
{ id: 'industrial', name: 'Industrial Park', type: 'industrial', emoji: '๐ญ' },
{ id: 'shopping', name: 'Shopping District', type: 'commercial', emoji: '๐๏ธ' }
];
zones.forEach(zone => this.zones.set(zone.id, zone));
}
// ๐ฏ Set up MQTT handlers
private setupEventHandlers(): void {
this.client.on('connect', () => {
console.log('๐๏ธ Smart City Dashboard Connected!');
this.subscribeToCity();
});
this.client.on('message', (topic, payload) => {
this.processCity Data(topic, payload);
});
}
// ๐ก Subscribe to city sensors
private subscribeToCity(): void {
const topics = [
'city/+/sensors/+/data', // ๐ Sensor data
'city/+/alerts', // ๐จ Zone alerts
'city/emergency/+', // ๐ Emergency services
'city/weather/current', // โ๏ธ Weather data
'city/events/+' // ๐ City events
];
this.client.subscribe(topics, { qos: 1 }, (err) => {
if (!err) {
console.log('๐ก Monitoring city sensors...');
}
});
}
// ๐ Process city data
private processCityData(topic: string, payload: Buffer): void {
const data = JSON.parse(payload.toString());
if (topic.includes('/sensors/') && topic.includes('/data')) {
this.handleSensorData(topic, data);
} else if (topic.includes('/alerts')) {
this.handleZoneAlert(topic, data);
} else if (topic.includes('/emergency/')) {
this.handleEmergency(topic, data);
}
}
// ๐ Handle sensor data
private handleSensorData(topic: string, data: any): void {
const [, zoneId, , sensorId] = topic.split('/');
const sensor = this.sensors.get(sensorId);
if (!sensor) return;
// ๐ Update metrics
this.metrics.set(sensorId, data.metrics);
// ๐ฏ Check thresholds
this.checkThresholds(sensor, data.metrics);
// ๐ Log status
this.logSensorStatus(sensor, data.metrics);
}
// ๐จ Check sensor thresholds
private checkThresholds(sensor: CitySensor, metrics: any): void {
let alert = null;
switch (sensor.type) {
case 'air_quality':
if (metrics.aqi > 150) {
alert = {
level: 'critical',
message: `๐จ Poor air quality in ${sensor.zoneId}: AQI ${metrics.aqi}`,
sensor: sensor,
value: metrics.aqi
};
}
break;
case 'traffic':
if (metrics.congestion > 0.8) {
alert = {
level: 'warning',
message: `๐ Heavy traffic in ${sensor.zoneId}: ${Math.round(metrics.congestion * 100)}% congestion`,
sensor: sensor,
value: metrics.congestion
};
}
break;
case 'parking':
const occupancy = metrics.occupied / metrics.total;
if (occupancy > 0.95) {
alert = {
level: 'info',
message: `๐
ฟ๏ธ Parking nearly full in ${sensor.zoneId}: ${metrics.occupied}/${metrics.total} spots`,
sensor: sensor,
value: occupancy
};
}
break;
}
if (alert) {
this.publishAlert(alert);
}
}
// ๐ข Publish alert
private publishAlert(alert: any): void {
this.alerts.push(alert);
this.client.publish(
`city/dashboard/alerts`,
JSON.stringify(alert),
{ qos: 1, retain: true }
);
console.log(`${this.getAlertEmoji(alert.level)} ${alert.message}`);
}
// ๐จ Get alert emoji
private getAlertEmoji(level: string): string {
const emojis: Record<string, string> = {
critical: '๐จ',
warning: 'โ ๏ธ',
info: 'โน๏ธ'
};
return emojis[level] || '๐ข';
}
// ๐ Get zone statistics
getZoneStats(zoneId: string): any {
const zone = this.zones.get(zoneId);
if (!zone) return null;
const zoneSensors = Array.from(this.sensors.values())
.filter(s => s.zoneId === zoneId);
const stats = {
zone: zone,
sensorCount: zoneSensors.length,
activeSensors: zoneSensors.filter(s => s.status === 'active').length,
metrics: {} as any,
health: 100
};
// Calculate average metrics
zoneSensors.forEach(sensor => {
const metrics = this.metrics.get(sensor.id);
if (metrics) {
// Aggregate metrics by type
if (!stats.metrics[sensor.type]) {
stats.metrics[sensor.type] = [];
}
stats.metrics[sensor.type].push(metrics);
}
});
return stats;
}
// ๐บ๏ธ Get city overview
getCityOverview(): void {
console.log('๐๏ธ Smart City Overview:');
console.log('========================');
this.zones.forEach(zone => {
const stats = this.getZoneStats(zone.id);
if (stats) {
console.log(`\n${zone.emoji} ${zone.name}:`);
console.log(` ๐ Sensors: ${stats.activeSensors}/${stats.sensorCount} active`);
console.log(` ๐ Health: ${stats.health}%`);
// Show key metrics
if (stats.metrics.air_quality) {
const avgAqi = stats.metrics.air_quality.reduce((sum: number, m: any) => sum + m.aqi, 0) / stats.metrics.air_quality.length;
console.log(` ๐ฌ๏ธ Air Quality: ${Math.round(avgAqi)} AQI`);
}
if (stats.metrics.traffic) {
const avgCongestion = stats.metrics.traffic.reduce((sum: number, m: any) => sum + m.congestion, 0) / stats.metrics.traffic.length;
console.log(` ๐ Traffic: ${Math.round(avgCongestion * 100)}% congestion`);
}
}
});
console.log(`\n๐ข Active Alerts: ${this.alerts.length}`);
}
// ๐ Log sensor status
private logSensorStatus(sensor: CitySensor, metrics: any): void {
const zone = this.zones.get(sensor.zoneId);
const emoji = this.getSensorEmoji(sensor.type);
let status = '';
switch (sensor.type) {
case 'traffic':
status = `Flow: ${metrics.flow} vehicles/hr, Congestion: ${Math.round(metrics.congestion * 100)}%`;
break;
case 'air_quality':
status = `AQI: ${metrics.aqi}, PM2.5: ${metrics.pm25}ยตg/mยณ`;
break;
case 'parking':
status = `${metrics.occupied}/${metrics.total} occupied (${Math.round((metrics.occupied/metrics.total) * 100)}%)`;
break;
case 'noise':
status = `${metrics.level}dB (avg: ${metrics.average}dB)`;
break;
}
console.log(`${emoji} ${zone?.emoji} ${sensor.type}: ${status}`);
}
// ๐จ Get sensor emoji
private getSensorEmoji(type: string): string {
const emojis: Record<string, string> = {
traffic: '๐ฆ',
air_quality: '๐ฌ๏ธ',
parking: '๐
ฟ๏ธ',
noise: '๐'
};
return emojis[type] || '๐';
}
// ๐ Handle emergency
private handleEmergency(topic: string, data: any): void {
console.log(`๐จ EMERGENCY: ${data.type} at ${data.location}`);
// Coordinate with traffic sensors
this.optimizeEmergencyRoute(data);
}
// ๐ Optimize emergency route
private optimizeEmergencyRoute(emergency: any): void {
// In a real system, this would coordinate traffic lights
console.log(`๐ฆ Optimizing route for ${emergency.type}...`);
this.client.publish(
'city/traffic/emergency_route',
JSON.stringify({
emergencyId: emergency.id,
route: emergency.route,
priority: 'high'
}),
{ qos: 2 }
);
}
}
// ๐ฎ Test the smart city dashboard!
const smartCity = new SmartCityDashboard('mqtt://localhost:1883');
// Simulate some sensors
const testSensors: CitySensor[] = [
{ id: 'traffic_001', type: 'traffic', zoneId: 'downtown', location: { lat: 40.7128, lng: -74.0060 }, status: 'active' },
{ id: 'air_001', type: 'air_quality', zoneId: 'industrial', location: { lat: 40.7260, lng: -74.0897 }, status: 'active' },
{ id: 'parking_001', type: 'parking', zoneId: 'shopping', location: { lat: 40.7580, lng: -73.9855 }, status: 'active' }
];
testSensors.forEach(sensor => smartCity['sensors'].set(sensor.id, sensor));
// Get city overview after some data
setTimeout(() => smartCity.getCityOverview(), 5000);
๐ Key Takeaways
Youโve learned so much! Hereโs what you can now do:
- โ Build IoT dashboards with MQTT and TypeScript ๐ช
- โ Handle real-time sensor data from multiple devices ๐ก๏ธ
- โ Implement secure MQTT connections for production use ๐ฏ
- โ Process and visualize IoT data streams ๐
- โ Create scalable architectures for IoT applications! ๐
Remember: MQTT is the backbone of modern IoT systems. Master it, and youโll be ready to build amazing connected applications! ๐ค
๐ค Next Steps
Congratulations! ๐ Youโve mastered IoT Dashboard development with MQTT integration!
Hereโs what to do next:
- ๐ป Build your own IoT monitoring system
- ๐๏ธ Explore MQTT brokers like Mosquitto or HiveMQ
- ๐ Learn about MQTT 5.0 features
- ๐ Try integrating with cloud IoT platforms (AWS IoT, Azure IoT Hub)
Remember: Every smart city, connected factory, and IoT innovation started with developers like you learning MQTT. Keep building, keep connecting, and most importantly, have fun creating the future of IoT! ๐
Happy IoT coding! ๐๐โจ