Smart grid management is like having an intelligent brain for your electrical network! ๐ง It monitors energy flow, balances supply and demand, and integrates renewable sources seamlessly. Letโs build a complete smart grid management system on Alpine Linux! ๐
What is a Smart Grid? ๐ค
A smart grid includes:
- Real-time monitoring - Track energy usage instantly
- Demand response - Adjust consumption automatically
- Renewable integration - Solar, wind, and battery systems
- Grid analytics - Predict and optimize energy flow
- Two-way communication - Devices talk to the grid
Think of it as upgrading your power grid to the digital age! โก
Installing Core Components ๐ฆ
Set up the smart grid infrastructure:
# Update package list
sudo apk update
# Install MQTT broker for device communication
sudo apk add mosquitto mosquitto-clients
# Install time-series database
sudo apk add influxdb telegraf
# Install Python for analytics
sudo apk add python3 py3-pip python3-dev
sudo apk add gcc musl-dev linux-headers
# Install Node.js for real-time dashboard
sudo apk add nodejs npm
# Install monitoring tools
sudo apk add prometheus grafana
Setting Up Energy Monitoring ๐
Create the monitoring infrastructure:
# Create project structure
mkdir -p ~/smart-grid/{devices,analytics,dashboard,config}
cd ~/smart-grid
# Smart meter simulator
cat > devices/smart_meter.py << 'EOF'
#!/usr/bin/env python3
import paho.mqtt.client as mqtt
import json
import time
import random
import math
from datetime import datetime
class SmartMeter:
def __init__(self, meter_id, location):
self.meter_id = meter_id
self.location = location
self.mqtt_client = mqtt.Client(f"meter_{meter_id}")
self.base_consumption = random.uniform(1.0, 3.0) # kW base load
def connect(self, broker="localhost", port=1883):
"""Connect to MQTT broker"""
self.mqtt_client.connect(broker, port, 60)
self.mqtt_client.loop_start()
print(f"Smart meter {self.meter_id} connected to broker")
def simulate_consumption(self):
"""Simulate realistic power consumption"""
hour = datetime.now().hour
# Daily consumption pattern
if 6 <= hour <= 9: # Morning peak
multiplier = 1.5
elif 17 <= hour <= 21: # Evening peak
multiplier = 2.0
elif 23 <= hour or hour <= 5: # Night low
multiplier = 0.5
else: # Day normal
multiplier = 1.0
# Add some randomness
consumption = self.base_consumption * multiplier
consumption += random.uniform(-0.2, 0.2)
# Simulate appliance usage spikes
if random.random() < 0.1: # 10% chance of spike
consumption += random.uniform(1.0, 3.0)
return max(0, consumption)
def read_meter(self):
"""Read current meter values"""
consumption = self.simulate_consumption()
voltage = 230 + random.uniform(-5, 5) # Voltage fluctuation
current = consumption * 1000 / voltage # Calculate current
power_factor = 0.9 + random.uniform(-0.05, 0.05)
frequency = 50 + random.uniform(-0.1, 0.1) # Grid frequency
data = {
"meter_id": self.meter_id,
"timestamp": datetime.now().isoformat(),
"location": self.location,
"consumption_kw": round(consumption, 3),
"voltage": round(voltage, 1),
"current": round(current, 2),
"power_factor": round(power_factor, 3),
"frequency": round(frequency, 2),
"energy_today_kwh": round(consumption * datetime.now().hour, 2)
}
return data
def publish_reading(self):
"""Publish meter reading to MQTT"""
data = self.read_meter()
topic = f"smartgrid/meters/{self.meter_id}/reading"
self.mqtt_client.publish(topic, json.dumps(data))
return data
def run(self, interval=5):
"""Run continuous monitoring"""
try:
while True:
reading = self.publish_reading()
print(f"Meter {self.meter_id}: {reading['consumption_kw']} kW")
time.sleep(interval)
except KeyboardInterrupt:
print(f"Meter {self.meter_id} shutting down")
self.mqtt_client.loop_stop()
self.mqtt_client.disconnect()
if __name__ == "__main__":
# Create multiple smart meters
meters = [
SmartMeter("SM001", "Building A"),
SmartMeter("SM002", "Building B"),
SmartMeter("SM003", "Building C")
]
# Connect all meters
for meter in meters:
meter.connect()
# Run first meter (in production, run each in separate process)
meters[0].run()
EOF
chmod +x devices/smart_meter.py
Solar Panel Integration โ๏ธ
Monitor renewable energy sources:
# Solar panel monitor
cat > devices/solar_monitor.py << 'EOF'
#!/usr/bin/env python3
import paho.mqtt.client as mqtt
import json
import time
import math
from datetime import datetime
class SolarPanel:
def __init__(self, panel_id, capacity_kw, location):
self.panel_id = panel_id
self.capacity_kw = capacity_kw
self.location = location
self.mqtt_client = mqtt.Client(f"solar_{panel_id}")
self.efficiency = 0.85 # 85% efficiency
def connect(self, broker="localhost"):
self.mqtt_client.connect(broker, 1883, 60)
self.mqtt_client.loop_start()
def calculate_solar_output(self):
"""Calculate solar panel output based on time of day"""
now = datetime.now()
hour = now.hour + now.minute / 60
# Sunrise at 6 AM, sunset at 6 PM (simplified)
if 6 <= hour <= 18:
# Solar intensity curve (bell curve)
peak_hour = 12
intensity = math.exp(-((hour - peak_hour) ** 2) / 18)
# Weather factor (random clouds)
weather_factor = 0.7 + random.uniform(0, 0.3)
# Calculate output
output = self.capacity_kw * intensity * weather_factor * self.efficiency
else:
output = 0 # No sun at night
return max(0, output)
def get_panel_data(self):
"""Get current panel data"""
output_kw = self.calculate_solar_output()
temperature = 25 + (output_kw / self.capacity_kw) * 20 # Panel temp
data = {
"panel_id": self.panel_id,
"timestamp": datetime.now().isoformat(),
"location": self.location,
"output_kw": round(output_kw, 3),
"capacity_kw": self.capacity_kw,
"efficiency": round(output_kw / self.capacity_kw * 100, 1),
"temperature_c": round(temperature, 1),
"voltage_dc": round(400 + output_kw * 10, 1),
"current_dc": round(output_kw * 1000 / 400, 2)
}
return data
def publish_data(self):
"""Publish solar panel data"""
data = self.get_panel_data()
topic = f"smartgrid/solar/{self.panel_id}/status"
self.mqtt_client.publish(topic, json.dumps(data))
return data
def run(self, interval=10):
"""Run continuous monitoring"""
try:
while True:
data = self.publish_data()
print(f"Solar {self.panel_id}: {data['output_kw']} kW ({data['efficiency']}%)")
time.sleep(interval)
except KeyboardInterrupt:
self.mqtt_client.loop_stop()
self.mqtt_client.disconnect()
# Battery storage system
class BatteryStorage:
def __init__(self, battery_id, capacity_kwh, max_power_kw):
self.battery_id = battery_id
self.capacity_kwh = capacity_kwh
self.max_power_kw = max_power_kw
self.current_charge_kwh = capacity_kwh * 0.5 # Start at 50%
self.mqtt_client = mqtt.Client(f"battery_{battery_id}")
def connect(self, broker="localhost"):
self.mqtt_client.connect(broker, 1883, 60)
self.mqtt_client.loop_start()
# Subscribe to grid commands
self.mqtt_client.on_message = self.on_message
self.mqtt_client.subscribe(f"smartgrid/battery/{self.battery_id}/command")
def on_message(self, client, userdata, msg):
"""Handle charging/discharging commands"""
try:
command = json.loads(msg.payload.decode())
if command["action"] == "charge":
self.charge(command.get("power_kw", 1.0))
elif command["action"] == "discharge":
self.discharge(command.get("power_kw", 1.0))
except Exception as e:
print(f"Error processing command: {e}")
def charge(self, power_kw):
"""Charge battery"""
power = min(power_kw, self.max_power_kw)
energy = power * (5/3600) # 5 second interval in hours
self.current_charge_kwh = min(
self.current_charge_kwh + energy,
self.capacity_kwh
)
def discharge(self, power_kw):
"""Discharge battery"""
power = min(power_kw, self.max_power_kw)
energy = power * (5/3600)
self.current_charge_kwh = max(
self.current_charge_kwh - energy,
0
)
def get_status(self):
"""Get battery status"""
soc = (self.current_charge_kwh / self.capacity_kwh) * 100
return {
"battery_id": self.battery_id,
"timestamp": datetime.now().isoformat(),
"state_of_charge": round(soc, 1),
"current_charge_kwh": round(self.current_charge_kwh, 2),
"capacity_kwh": self.capacity_kwh,
"max_power_kw": self.max_power_kw,
"available_power_kw": self.max_power_kw if soc > 10 else 0,
"status": "charging" if soc < 90 else "ready"
}
def publish_status(self):
"""Publish battery status"""
status = self.get_status()
topic = f"smartgrid/battery/{self.battery_id}/status"
self.mqtt_client.publish(topic, json.dumps(status))
return status
if __name__ == "__main__":
import random
# Create solar panel
solar = SolarPanel("SOLAR001", 10.0, "Rooftop A")
solar.connect()
# Create battery
battery = BatteryStorage("BAT001", 50.0, 5.0)
battery.connect()
# Run solar panel monitoring
solar.run()
EOF
Grid Analytics Engine ๐งฎ
Process and analyze grid data:
# Grid analytics
cat > analytics/grid_analytics.py << 'EOF'
#!/usr/bin/env python3
import paho.mqtt.client as mqtt
import json
from influxdb_client import InfluxDBClient, Point
from datetime import datetime, timedelta
import numpy as np
from collections import defaultdict
class GridAnalytics:
def __init__(self):
self.mqtt_client = mqtt.Client("grid_analytics")
self.influx_client = InfluxDBClient(
url="http://localhost:8086",
token="your-token",
org="smartgrid"
)
self.write_api = self.influx_client.write_api()
# Real-time data storage
self.meter_data = defaultdict(dict)
self.solar_data = defaultdict(dict)
self.battery_data = defaultdict(dict)
# Grid statistics
self.total_consumption = 0
self.total_generation = 0
self.grid_frequency = 50.0
def connect(self):
"""Connect to MQTT broker"""
self.mqtt_client.on_connect = self.on_connect
self.mqtt_client.on_message = self.on_message
self.mqtt_client.connect("localhost", 1883, 60)
self.mqtt_client.loop_start()
def on_connect(self, client, userdata, flags, rc):
"""Subscribe to all grid topics"""
print("Connected to MQTT broker")
client.subscribe("smartgrid/+/+/+")
def on_message(self, client, userdata, msg):
"""Process incoming messages"""
try:
topic_parts = msg.topic.split('/')
device_type = topic_parts[1]
device_id = topic_parts[2]
data = json.loads(msg.payload.decode())
# Store data by type
if device_type == "meters":
self.process_meter_data(device_id, data)
elif device_type == "solar":
self.process_solar_data(device_id, data)
elif device_type == "battery":
self.process_battery_data(device_id, data)
# Run analytics
self.analyze_grid_state()
except Exception as e:
print(f"Error processing message: {e}")
def process_meter_data(self, meter_id, data):
"""Process smart meter data"""
self.meter_data[meter_id] = data
# Store in InfluxDB
point = Point("meter_reading") \
.tag("meter_id", meter_id) \
.tag("location", data.get("location", "unknown")) \
.field("consumption_kw", data["consumption_kw"]) \
.field("voltage", data["voltage"]) \
.field("current", data["current"]) \
.field("power_factor", data["power_factor"]) \
.field("frequency", data["frequency"]) \
.time(datetime.utcnow())
self.write_api.write(bucket="smartgrid", record=point)
def process_solar_data(self, panel_id, data):
"""Process solar panel data"""
self.solar_data[panel_id] = data
# Store in InfluxDB
point = Point("solar_output") \
.tag("panel_id", panel_id) \
.tag("location", data.get("location", "unknown")) \
.field("output_kw", data["output_kw"]) \
.field("efficiency", data["efficiency"]) \
.field("temperature_c", data["temperature_c"]) \
.time(datetime.utcnow())
self.write_api.write(bucket="smartgrid", record=point)
def process_battery_data(self, battery_id, data):
"""Process battery data"""
self.battery_data[battery_id] = data
# Store in InfluxDB
point = Point("battery_status") \
.tag("battery_id", battery_id) \
.field("state_of_charge", data["state_of_charge"]) \
.field("current_charge_kwh", data["current_charge_kwh"]) \
.field("available_power_kw", data["available_power_kw"]) \
.time(datetime.utcnow())
self.write_api.write(bucket="smartgrid", record=point)
def analyze_grid_state(self):
"""Analyze current grid state"""
# Calculate totals
self.total_consumption = sum(
m["consumption_kw"] for m in self.meter_data.values()
)
self.total_generation = sum(
s["output_kw"] for s in self.solar_data.values()
)
# Grid balance
grid_balance = self.total_generation - self.total_consumption
# Publish grid state
grid_state = {
"timestamp": datetime.now().isoformat(),
"total_consumption_kw": round(self.total_consumption, 2),
"total_generation_kw": round(self.total_generation, 2),
"grid_balance_kw": round(grid_balance, 2),
"renewable_percentage": round(
(self.total_generation / self.total_consumption * 100)
if self.total_consumption > 0 else 0, 1
),
"num_meters": len(self.meter_data),
"num_solar": len(self.solar_data),
"num_batteries": len(self.battery_data)
}
self.mqtt_client.publish(
"smartgrid/analytics/state",
json.dumps(grid_state)
)
# Demand response signals
if grid_balance < -5: # High demand
self.trigger_demand_response("reduce", abs(grid_balance))
elif grid_balance > 5: # Excess generation
self.trigger_battery_charging(grid_balance)
def trigger_demand_response(self, action, amount_kw):
"""Send demand response signals"""
dr_signal = {
"timestamp": datetime.now().isoformat(),
"action": action,
"amount_kw": amount_kw,
"duration_minutes": 15
}
self.mqtt_client.publish(
"smartgrid/demand_response/signal",
json.dumps(dr_signal)
)
def trigger_battery_charging(self, excess_kw):
"""Manage battery charging with excess power"""
available_batteries = [
b for b in self.battery_data.values()
if b["state_of_charge"] < 90
]
if available_batteries:
power_per_battery = excess_kw / len(available_batteries)
for battery in available_batteries:
command = {
"action": "charge",
"power_kw": min(power_per_battery, battery["max_power_kw"])
}
self.mqtt_client.publish(
f"smartgrid/battery/{battery['battery_id']}/command",
json.dumps(command)
)
def predict_demand(self):
"""Predict future energy demand"""
# Query historical data
query = '''
from(bucket: "smartgrid")
|> range(start: -7d)
|> filter(fn: (r) => r._measurement == "meter_reading")
|> filter(fn: (r) => r._field == "consumption_kw")
|> aggregateWindow(every: 1h, fn: mean)
'''
# Simple prediction based on historical patterns
# In production, use ML models
def calculate_cost_optimization(self):
"""Calculate optimal energy usage based on pricing"""
# Time-of-use pricing
hour = datetime.now().hour
if 17 <= hour <= 21: # Peak hours
price_per_kwh = 0.25
elif 9 <= hour <= 17: # Mid-peak
price_per_kwh = 0.15
else: # Off-peak
price_per_kwh = 0.10
current_cost = self.total_consumption * price_per_kwh
return {
"current_price_per_kwh": price_per_kwh,
"current_hourly_cost": round(current_cost, 2),
"recommendation": "Shift non-critical loads to off-peak hours"
}
if __name__ == "__main__":
analytics = GridAnalytics()
analytics.connect()
print("Grid Analytics Engine running...")
try:
while True:
time.sleep(60)
# Periodic reports
cost = analytics.calculate_cost_optimization()
print(f"Current cost: ${cost['current_hourly_cost']}/hour")
except KeyboardInterrupt:
print("Shutting down analytics engine")
EOF
Real-time Dashboard ๐ฑ
Create a web dashboard for grid monitoring:
# Dashboard backend
cd dashboard
npm init -y
npm install express socket.io mqtt influx
cat > server.js << 'EOF'
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const mqtt = require('mqtt');
const { InfluxDB } = require('@influxdata/influxdb-client');
const app = express();
const server = http.createServer(app);
const io = socketIo(server);
// MQTT client
const mqttClient = mqtt.connect('mqtt://localhost:1883');
// InfluxDB client
const influxDB = new InfluxDB({
url: 'http://localhost:8086',
token: 'your-token'
});
const queryApi = influxDB.getQueryApi('smartgrid');
// Serve static files
app.use(express.static('public'));
// Grid state storage
let gridState = {
totalConsumption: 0,
totalGeneration: 0,
gridBalance: 0,
renewablePercentage: 0,
meters: {},
solar: {},
batteries: {}
};
// MQTT subscriptions
mqttClient.on('connect', () => {
console.log('Connected to MQTT broker');
mqttClient.subscribe('smartgrid/+/+/+');
mqttClient.subscribe('smartgrid/analytics/+');
});
mqttClient.on('message', (topic, message) => {
try {
const data = JSON.parse(message.toString());
const topicParts = topic.split('/');
if (topic === 'smartgrid/analytics/state') {
gridState = { ...gridState, ...data };
io.emit('gridState', gridState);
} else if (topicParts[1] === 'meters') {
gridState.meters[topicParts[2]] = data;
io.emit('meterUpdate', { id: topicParts[2], data });
} else if (topicParts[1] === 'solar') {
gridState.solar[topicParts[2]] = data;
io.emit('solarUpdate', { id: topicParts[2], data });
} else if (topicParts[1] === 'battery') {
gridState.batteries[topicParts[2]] = data;
io.emit('batteryUpdate', { id: topicParts[2], data });
}
} catch (error) {
console.error('Error processing message:', error);
}
});
// API endpoints
app.get('/api/state', (req, res) => {
res.json(gridState);
});
app.get('/api/history/:measurement/:field', async (req, res) => {
const { measurement, field } = req.params;
const { start = '-1h' } = req.query;
const query = `
from(bucket: "smartgrid")
|> range(start: ${start})
|> filter(fn: (r) => r._measurement == "${measurement}")
|> filter(fn: (r) => r._field == "${field}")
|> aggregateWindow(every: 1m, fn: mean)
`;
try {
const result = [];
await queryApi.collectRows(query).then(rows => {
rows.forEach(row => {
result.push({
time: row._time,
value: row._value,
tag: row.meter_id || row.panel_id || row.battery_id
});
});
});
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Socket.io connection
io.on('connection', (socket) => {
console.log('Client connected');
// Send current state
socket.emit('gridState', gridState);
// Handle control commands
socket.on('controlCommand', (command) => {
mqttClient.publish(
`smartgrid/${command.device}/${command.id}/command`,
JSON.stringify(command.payload)
);
});
socket.on('disconnect', () => {
console.log('Client disconnected');
});
});
server.listen(3000, () => {
console.log('Smart Grid Dashboard running on port 3000');
});
EOF
# Dashboard frontend
mkdir -p public
cat > public/index.html << 'EOF'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart Grid Dashboard</title>
<script src="/socket.io/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #0a0a0a;
color: #fff;
}
.header {
background: #1a1a1a;
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #333;
}
.container {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.grid-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: #1a1a1a;
border-radius: 12px;
padding: 1.5rem;
border: 1px solid #333;
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
border-color: #4CAF50;
}
.stat-value {
font-size: 2.5rem;
font-weight: bold;
margin: 0.5rem 0;
}
.stat-label {
color: #888;
text-transform: uppercase;
font-size: 0.85rem;
letter-spacing: 1px;
}
.positive { color: #4CAF50; }
.negative { color: #f44336; }
.charts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.chart-container {
background: #1a1a1a;
border-radius: 12px;
padding: 1.5rem;
border: 1px solid #333;
}
.devices {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 1.5rem;
}
.device-group {
background: #1a1a1a;
border-radius: 12px;
padding: 1.5rem;
border: 1px solid #333;
}
.device-item {
background: #0a0a0a;
padding: 1rem;
margin: 0.5rem 0;
border-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
}
.device-status {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 0.5rem;
}
.status-online { background: #4CAF50; }
.status-offline { background: #f44336; }
.solar-icon { color: #FFC107; }
.battery-icon { color: #2196F3; }
.meter-icon { color: #9C27B0; }
button {
background: #4CAF50;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
}
button:hover {
background: #45a049;
}
.alert {
background: #f44336;
padding: 1rem;
border-radius: 8px;
margin: 1rem 0;
display: flex;
align-items: center;
}
.alert-icon {
font-size: 1.5rem;
margin-right: 1rem;
}
</style>
</head>
<body>
<header class="header">
<h1>โก Smart Grid Control Center</h1>
<div>
<span id="time"></span>
<span style="margin-left: 2rem;">Status: <span id="connection-status">Connecting...</span></span>
</div>
</header>
<div class="container">
<!-- Grid Statistics -->
<div class="grid-stats">
<div class="stat-card">
<div class="stat-label">Total Consumption</div>
<div class="stat-value" id="total-consumption">0.0</div>
<div>kW</div>
</div>
<div class="stat-card">
<div class="stat-label">Solar Generation</div>
<div class="stat-value positive" id="total-generation">0.0</div>
<div>kW</div>
</div>
<div class="stat-card">
<div class="stat-label">Grid Balance</div>
<div class="stat-value" id="grid-balance">0.0</div>
<div>kW</div>
</div>
<div class="stat-card">
<div class="stat-label">Renewable %</div>
<div class="stat-value positive" id="renewable-percentage">0</div>
<div>Percent</div>
</div>
</div>
<!-- Alerts -->
<div id="alerts"></div>
<!-- Charts -->
<div class="charts">
<div class="chart-container">
<h3>Power Flow</h3>
<canvas id="power-chart"></canvas>
</div>
<div class="chart-container">
<h3>Energy Sources</h3>
<canvas id="sources-chart"></canvas>
</div>
</div>
<!-- Devices -->
<div class="devices">
<!-- Smart Meters -->
<div class="device-group">
<h3><span class="meter-icon">๐</span> Smart Meters</h3>
<div id="meters-list"></div>
</div>
<!-- Solar Panels -->
<div class="device-group">
<h3><span class="solar-icon">โ๏ธ</span> Solar Panels</h3>
<div id="solar-list"></div>
</div>
<!-- Batteries -->
<div class="device-group">
<h3><span class="battery-icon">๐</span> Battery Storage</h3>
<div id="batteries-list"></div>
</div>
</div>
</div>
<script>
const socket = io();
let powerChart, sourcesChart;
let gridState = {};
// Initialize charts
function initCharts() {
// Power flow chart
const powerCtx = document.getElementById('power-chart').getContext('2d');
powerChart = new Chart(powerCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Consumption',
data: [],
borderColor: '#f44336',
tension: 0.1
}, {
label: 'Generation',
data: [],
borderColor: '#4CAF50',
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true }
}
}
});
// Energy sources chart
const sourcesCtx = document.getElementById('sources-chart').getContext('2d');
sourcesChart = new Chart(sourcesCtx, {
type: 'doughnut',
data: {
labels: ['Solar', 'Grid', 'Battery'],
datasets: [{
data: [0, 0, 0],
backgroundColor: ['#FFC107', '#9E9E9E', '#2196F3']
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
}
// Update display
function updateDisplay(state) {
document.getElementById('total-consumption').textContent =
state.total_consumption_kw?.toFixed(1) || '0.0';
document.getElementById('total-generation').textContent =
state.total_generation_kw?.toFixed(1) || '0.0';
const balance = state.grid_balance_kw || 0;
const balanceEl = document.getElementById('grid-balance');
balanceEl.textContent = balance.toFixed(1);
balanceEl.className = balance >= 0 ? 'stat-value positive' : 'stat-value negative';
document.getElementById('renewable-percentage').textContent =
Math.round(state.renewable_percentage || 0);
// Update charts
updateCharts(state);
// Update device lists
updateDeviceLists(state);
// Check for alerts
checkAlerts(state);
}
function updateCharts(state) {
// Add data point to power chart
const time = new Date().toLocaleTimeString();
if (powerChart.data.labels.length > 20) {
powerChart.data.labels.shift();
powerChart.data.datasets[0].data.shift();
powerChart.data.datasets[1].data.shift();
}
powerChart.data.labels.push(time);
powerChart.data.datasets[0].data.push(state.total_consumption_kw || 0);
powerChart.data.datasets[1].data.push(state.total_generation_kw || 0);
powerChart.update();
// Update sources chart
const solarPower = state.total_generation_kw || 0;
const gridPower = Math.max(0, (state.total_consumption_kw || 0) - solarPower);
const batteryPower = 0; // Calculate from battery data
sourcesChart.data.datasets[0].data = [solarPower, gridPower, batteryPower];
sourcesChart.update();
}
function updateDeviceLists(state) {
// Update meters
const metersList = document.getElementById('meters-list');
metersList.innerHTML = '';
Object.entries(state.meters || {}).forEach(([id, meter]) => {
const item = document.createElement('div');
item.className = 'device-item';
item.innerHTML = `
<div>
<span class="device-status status-online"></span>
${id} - ${meter.location || 'Unknown'}
</div>
<div>${meter.consumption_kw?.toFixed(2) || 0} kW</div>
`;
metersList.appendChild(item);
});
// Update solar panels
const solarList = document.getElementById('solar-list');
solarList.innerHTML = '';
Object.entries(state.solar || {}).forEach(([id, solar]) => {
const item = document.createElement('div');
item.className = 'device-item';
item.innerHTML = `
<div>
<span class="device-status status-online"></span>
${id} - ${solar.location || 'Unknown'}
</div>
<div>${solar.output_kw?.toFixed(2) || 0} kW (${solar.efficiency || 0}%)</div>
`;
solarList.appendChild(item);
});
// Update batteries
const batteriesList = document.getElementById('batteries-list');
batteriesList.innerHTML = '';
Object.entries(state.batteries || {}).forEach(([id, battery]) => {
const item = document.createElement('div');
item.className = 'device-item';
item.innerHTML = `
<div>
<span class="device-status status-online"></span>
${id}
</div>
<div>
${battery.state_of_charge?.toFixed(1) || 0}%
<button onclick="chargeBattery('${id}')">Charge</button>
</div>
`;
batteriesList.appendChild(item);
});
}
function checkAlerts(state) {
const alertsDiv = document.getElementById('alerts');
alertsDiv.innerHTML = '';
// Check for high demand
if (state.grid_balance_kw < -10) {
const alert = document.createElement('div');
alert.className = 'alert';
alert.innerHTML = `
<span class="alert-icon">โ ๏ธ</span>
<div>
<strong>High Demand Alert</strong><br>
Grid demand exceeds generation by ${Math.abs(state.grid_balance_kw).toFixed(1)} kW
</div>
`;
alertsDiv.appendChild(alert);
}
}
function chargeBattery(batteryId) {
socket.emit('controlCommand', {
device: 'battery',
id: batteryId,
payload: { action: 'charge', power_kw: 2.0 }
});
}
// Socket events
socket.on('connect', () => {
document.getElementById('connection-status').textContent = 'Connected';
});
socket.on('disconnect', () => {
document.getElementById('connection-status').textContent = 'Disconnected';
});
socket.on('gridState', (state) => {
gridState = state;
updateDisplay(state);
});
socket.on('meterUpdate', (data) => {
if (!gridState.meters) gridState.meters = {};
gridState.meters[data.id] = data.data;
updateDisplay(gridState);
});
socket.on('solarUpdate', (data) => {
if (!gridState.solar) gridState.solar = {};
gridState.solar[data.id] = data.data;
updateDisplay(gridState);
});
socket.on('batteryUpdate', (data) => {
if (!gridState.batteries) gridState.batteries = {};
gridState.batteries[data.id] = data.data;
updateDisplay(gridState);
});
// Update time
setInterval(() => {
document.getElementById('time').textContent = new Date().toLocaleString();
}, 1000);
// Initialize
initCharts();
</script>
</body>
</html>
EOF
Grid Automation Rules ๐ค
Implement smart grid automation:
# Automation rules engine
cat > analytics/automation_rules.py << 'EOF'
#!/usr/bin/env python3
import json
from datetime import datetime, time
class GridAutomation:
def __init__(self, mqtt_client):
self.mqtt_client = mqtt_client
self.rules = self.load_rules()
def load_rules(self):
"""Load automation rules"""
return [
{
"name": "Peak Shaving",
"condition": lambda state: state["total_consumption_kw"] > 50,
"action": self.peak_shaving
},
{
"name": "Solar Excess Storage",
"condition": lambda state: state["grid_balance_kw"] > 5,
"action": self.store_excess_solar
},
{
"name": "Night Load Balancing",
"condition": lambda state: self.is_night_time() and state["total_consumption_kw"] > 30,
"action": self.night_load_balance
},
{
"name": "Emergency Response",
"condition": lambda state: state.get("grid_frequency", 50) < 49.5,
"action": self.emergency_response
}
]
def is_night_time(self):
"""Check if it's night time"""
current_time = datetime.now().time()
return current_time < time(6, 0) or current_time > time(22, 0)
def evaluate_rules(self, grid_state):
"""Evaluate all automation rules"""
for rule in self.rules:
if rule["condition"](grid_state):
print(f"Triggering rule: {rule['name']}")
rule["action"](grid_state)
def peak_shaving(self, state):
"""Reduce peak demand"""
# Discharge batteries
for battery_id in state.get("batteries", {}):
command = {
"action": "discharge",
"power_kw": 5.0
}
self.mqtt_client.publish(
f"smartgrid/battery/{battery_id}/command",
json.dumps(command)
)
# Send demand response signal
dr_signal = {
"action": "reduce",
"target_reduction_kw": 10,
"incentive_per_kwh": 0.50
}
self.mqtt_client.publish(
"smartgrid/demand_response/signal",
json.dumps(dr_signal)
)
def store_excess_solar(self, state):
"""Store excess solar in batteries"""
excess_kw = state["grid_balance_kw"]
# Charge available batteries
available_batteries = [
b for b in state.get("batteries", {}).values()
if b["state_of_charge"] < 90
]
if available_batteries:
power_per_battery = excess_kw / len(available_batteries)
for battery in available_batteries:
command = {
"action": "charge",
"power_kw": min(power_per_battery, 5.0)
}
self.mqtt_client.publish(
f"smartgrid/battery/{battery['battery_id']}/command",
json.dumps(command)
)
def night_load_balance(self, state):
"""Balance load during night hours"""
# Shift flexible loads to night time
load_shift_signal = {
"action": "shift_to_now",
"device_types": ["water_heater", "ev_charger"],
"duration_hours": 4
}
self.mqtt_client.publish(
"smartgrid/load_control/signal",
json.dumps(load_shift_signal)
)
def emergency_response(self, state):
"""Emergency grid stabilization"""
# Shed non-critical loads
emergency_signal = {
"action": "emergency_shed",
"priority_levels": [3, 4, 5], # Shed lowest priority loads
"reason": "Grid frequency drop"
}
self.mqtt_client.publish(
"smartgrid/emergency/signal",
json.dumps(emergency_signal)
)
# Discharge all available batteries
for battery_id in state.get("batteries", {}):
command = {
"action": "discharge",
"power_kw": 10.0, # Maximum discharge
"emergency": True
}
self.mqtt_client.publish(
f"smartgrid/battery/{battery_id}/command",
json.dumps(command)
)
EOF
Starting the Smart Grid System ๐
Create startup scripts:
# System startup script
cat > start_smartgrid.sh << 'EOF'
#!/bin/sh
# Start Smart Grid Management System
echo "Starting Smart Grid Management System..."
# Start MQTT broker
mosquitto -d
# Start InfluxDB
influxd &
# Start smart meters (in production, these would be real devices)
python3 devices/smart_meter.py &
python3 devices/solar_monitor.py &
# Start analytics engine
python3 analytics/grid_analytics.py &
# Start web dashboard
cd dashboard && npm start &
echo "Smart Grid System started!"
echo "Dashboard available at http://localhost:3000"
# Monitor logs
tail -f /var/log/mosquitto/mosquitto.log
EOF
chmod +x start_smartgrid.sh
Best Practices ๐
- Redundancy - Multiple monitoring points
- Security - Encrypt all communications
- Scalability - Design for growth
- Real-time processing - Minimize latency
- Data retention - Balance storage vs. analysis needs
Troubleshooting ๐ง
MQTT Connection Issues
# Test MQTT broker
mosquitto_sub -t "smartgrid/#" -v
# Check MQTT logs
sudo tail -f /var/log/mosquitto/mosquitto.log
# Test publishing
mosquitto_pub -t "smartgrid/test" -m "Hello Grid"
Data Not Appearing
# Check InfluxDB
influx
> SHOW DATABASES
> USE smartgrid
> SHOW MEASUREMENTS
> SELECT * FROM meter_reading LIMIT 10
Quick Commands ๐
# Monitor all MQTT messages
mosquitto_sub -t "smartgrid/#" -v
# Check grid state
curl http://localhost:3000/api/state | jq
# Manually control battery
mosquitto_pub -t "smartgrid/battery/BAT001/command" -m '{"action":"charge","power_kw":5}'
# View analytics
curl http://localhost:3000/api/history/meter_reading/consumption_kw?start=-1h
Conclusion ๐ฏ
Youโve built a complete smart grid management system on Alpine Linux! From real-time monitoring to renewable integration and intelligent automation, your grid can now optimize energy distribution automatically. This system forms the foundation for a sustainable, efficient electrical infrastructure. Keep innovating with clean energy! โกโจ