Real-time Updates¶
Django Micboard provides real-time monitoring of Shure wireless microphone systems through WebSocket connections and automatic data synchronization.
WebSocket Architecture¶
Connection Overview¶
Shure System API → Django Micboard → WebSocket → Frontend
↑ ↓ ↓
Device Polling Database Updates Live Updates
WebSocket Endpoints¶
- Device Updates:
ws://your-server/ws/devices/ - Connection Status:
ws://your-server/ws/connections/ - System Health:
ws://your-server/ws/health/
Real-time Data Types¶
Device Metrics¶
Battery Information:
{
"type": "device_update",
"device_id": "SHURE001",
"battery_level": 85,
"charging": false,
"battery_status": "good"
}
RF Signal Data:
{
"type": "device_update",
"device_id": "SHURE001",
"rf_signal": -45,
"rf_quality": "excellent",
"interference": false
}
Audio Levels:
{
"type": "device_update",
"device_id": "SHURE001",
"audio_level": -12,
"peak_level": -6,
"mute_status": false
}
Connection Status¶
WebSocket Connection Health:
{
"type": "connection_update",
"manufacturer": "shure",
"status": "connected",
"latency_ms": 45,
"last_update": "2024-01-22T10:30:00Z"
}
API Connection Status:
{
"type": "api_health",
"endpoint": "https://shure-system.local",
"status": "healthy",
"response_time_ms": 234,
"error_count": 0
}
Frontend Integration¶
JavaScript WebSocket Client¶
class MicboardWebSocket {
constructor() {
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
this.ws = new WebSocket('ws://your-server/ws/devices/');
this.ws.onopen = () => {
console.log('Connected to Micboard WebSocket');
this.reconnectAttempts = 0;
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
};
this.ws.onclose = () => {
console.log('WebSocket disconnected');
this.attemptReconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
handleMessage(data) {
switch(data.type) {
case 'device_update':
this.updateDeviceDisplay(data);
break;
case 'connection_update':
this.updateConnectionStatus(data);
break;
case 'system_alert':
this.showAlert(data);
break;
}
}
updateDeviceDisplay(data) {
const deviceElement = document.getElementById(`device-${data.device_id}`);
if (deviceElement) {
// Update battery level
const batteryBar = deviceElement.querySelector('.battery-bar');
batteryBar.style.width = `${data.battery_level}%`;
// Update signal strength
const signalIcon = deviceElement.querySelector('.signal-icon');
signalIcon.className = `signal-icon ${data.rf_quality}`;
// Update timestamp
const timestamp = deviceElement.querySelector('.last-update');
timestamp.textContent = new Date().toLocaleTimeString();
}
}
attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
setTimeout(() => {
console.log(`Attempting WebSocket reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connect();
}, delay);
}
}
}
// Initialize
const micboardWS = new MicboardWebSocket();
micboardWS.connect();
React Component Example¶
import React, { useEffect, useState } from 'react';
function DeviceMonitor({ deviceId }) {
const [deviceData, setDeviceData] = useState(null);
const [connectionStatus, setConnectionStatus] = useState('disconnected');
useEffect(() => {
const ws = new WebSocket('ws://your-server/ws/devices/');
ws.onopen = () => setConnectionStatus('connected');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.device_id === deviceId) {
setDeviceData(data);
}
};
ws.onclose = () => setConnectionStatus('disconnected');
return () => ws.close();
}, [deviceId]);
if (!deviceData) return <div>Loading...</div>;
return (
<div className="device-monitor">
<h3>{deviceData.name}</h3>
<div className="metrics">
<div className="battery">
Battery: {deviceData.battery_level}%
<div className="battery-bar" style={{width: `${deviceData.battery_level}%`}} />
</div>
<div className="signal">
RF: {deviceData.rf_signal} dBm ({deviceData.rf_quality})
</div>
<div className="audio">
Audio: {deviceData.audio_level} dB
</div>
</div>
<div className="status">
Status: {connectionStatus}
</div>
</div>
);
}
Backend Configuration¶
Django Channels Setup¶
settings.py:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
},
},
}
asgi.py:
from channels.routing import ProtocolTypeRouter, URLRouter
from micboard.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': URLRouter(websocket_urlpatterns),
})
WebSocket Consumers¶
# micboard/consumers.py
from channels.generic.websocket import JsonWebsocketConsumer
from micboard.services import DeviceService
class DeviceConsumer(JsonWebsocketConsumer):
def connect(self):
self.accept()
# Send initial device data
devices = DeviceService.get_all_devices()
self.send_json({
'type': 'initial_data',
'devices': devices
})
def disconnect(self, close_code):
pass
def device_update(self, event):
# Send device update to WebSocket
self.send_json(event['data'])
Data Synchronization¶
Polling Strategy¶
Continuous Polling:
# Poll every 30 seconds
python manage.py poll_devices --manufacturer shure --continuous --interval 30
Adaptive Polling: - Normal devices: 30-second intervals - Critical devices: 10-second intervals - Offline devices: 60-second intervals
Change Detection¶
Efficient Updates: - Only send changed data - Use ETags for API polling - Implement diff-based updates
def send_device_update(device, changes):
# Only send changed fields
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'devices',
{
'type': 'device_update',
'data': {
'device_id': device.device_id,
'changes': changes,
'timestamp': timezone.now().isoformat()
}
}
)
Performance Optimization¶
Connection Pooling¶
Redis Configuration:
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
'hosts': [('127.0.0.1', 6379)],
'capacity': 1000, # Connection pool size
'expiry': 300, # Connection expiry
},
},
}
Message Batching¶
Batch Updates:
def batch_device_updates(updates):
# Group updates by time window
batches = group_updates_by_time(updates, window_seconds=1)
for batch in batches:
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'devices',
{
'type': 'batch_update',
'updates': batch
}
)
Client-side Optimization¶
Update Throttling:
class UpdateThrottler {
constructor(delay = 100) {
this.delay = delay;
this.timeout = null;
this.pendingUpdates = {};
}
queueUpdate(deviceId, update) {
this.pendingUpdates[deviceId] = update;
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.flushUpdates();
}, this.delay);
}
flushUpdates() {
// Send batched updates
Object.entries(this.pendingUpdates).forEach(([deviceId, update]) => {
this.applyUpdate(deviceId, update);
});
this.pendingUpdates = {};
}
}
Monitoring and Debugging¶
WebSocket Debugging¶
Browser Developer Tools: - Network tab: WebSocket connections - Console: Connection events and messages - Application tab: WebSocket frames
Django Debug Toolbar:
# settings.py
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
Connection Health Monitoring¶
Health Checks:
from channels.layers import get_channel_layer
def check_websocket_health():
channel_layer = get_channel_layer()
# Test channel layer connectivity
try:
async_to_sync(channel_layer.send)('health_check', {'ping': True})
return True
except Exception as e:
logger.error(f"WebSocket health check failed: {e}")
return False
Troubleshooting¶
Connection Issues¶
WebSocket won't connect: - Check ASGI configuration - Verify Redis is running - Confirm firewall settings - Check SSL certificate validity
Updates not received: - Verify channel layer configuration - Check consumer group subscriptions - Monitor Redis connection pool - Review message serialization
Performance Issues¶
High latency: - Optimize database queries - Implement message caching - Use connection pooling - Monitor Redis performance
Memory usage: - Implement message cleanup - Use efficient serialization - Monitor connection counts - Configure appropriate timeouts
Common Patterns¶
Heartbeat Implementation:
// Client heartbeat
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({type: 'ping'}));
}
}, 30000);
// Server heartbeat response
def receive(self, text_data):
data = json.loads(text_data)
if data.get('type') == 'ping':
self.send_json({'type': 'pong'})
Reconnection Logic:
class ReconnectingWebSocket {
constructor(url, options = {}) {
this.url = url;
this.options = {
maxReconnectAttempts: 5,
reconnectInterval: 1000,
maxReconnectInterval: 30000,
...options
};
this.reconnectAttempts = 0;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
// ... connection handlers
}
handleClose() {
if (this.reconnectAttempts < this.options.maxReconnectAttempts) {
const delay = Math.min(
this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts),
this.options.maxReconnectInterval
);
setTimeout(() => {
this.reconnectAttempts++;
this.connect();
}, delay);
}
}
}