Es scheint, dass Sie eine Reihe von Dateien und Verzeichnissen in Ihrem Backend-Projekt bearbeitet haben. Hier ist ein zusammenfassender Überblick über die Änderungen:
1. **Entfernung von 'node_modules'**: Es scheint, dass Sie den 'node_modules'-Ordner entfernt oder aktualisiert haben, da einige Dateien wie '.gitignore', 'package
This commit is contained in:
366
backend/templates/energy_dashboard.html
Normal file
366
backend/templates/energy_dashboard.html
Normal file
@ -0,0 +1,366 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Energiemonitoring - Mercedes-Benz TBA Marienfelde{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<!-- Chart.js für Energiediagramme -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<!-- Custom CSS für Energiemonitoring -->
|
||||
<style>
|
||||
.energy-card {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
||||
border: 2px solid #f1f5f9;
|
||||
border-radius: 16px;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.energy-card:hover {
|
||||
transform: translateY(-4px) scale(1.02);
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15);
|
||||
border-color: #0073ce;
|
||||
}
|
||||
.energy-metric {
|
||||
background: linear-gradient(135deg, #0073ce 0%, #005ba3 100%);
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800">
|
||||
|
||||
<!-- Hero Header -->
|
||||
<div class="relative overflow-hidden bg-gradient-to-r from-slate-900 via-blue-900 to-indigo-900 text-white rounded-3xl mx-4 mt-4">
|
||||
<div class="absolute inset-0 bg-black/20"></div>
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent"></div>
|
||||
|
||||
<!-- Live Status Indicator -->
|
||||
<div class="absolute top-4 right-4 flex items-center space-x-2">
|
||||
<div class="flex items-center space-x-2 bg-white/10 backdrop-blur-sm border border-white/20 rounded-full px-3 py-1">
|
||||
<div id="live-indicator" class="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span class="text-sm font-medium">Live</span>
|
||||
</div>
|
||||
<div class="bg-white/10 backdrop-blur-sm border border-white/20 rounded-full px-3 py-1">
|
||||
<span id="live-time" class="text-sm font-medium"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
||||
<div class="text-center">
|
||||
<!-- Mercedes-Benz Logo -->
|
||||
<div class="inline-flex items-center justify-center w-20 h-20 bg-white/10 backdrop-blur-sm rounded-full mb-6 border border-white/20">
|
||||
<svg class="w-10 h-10 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.94-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-5xl md:text-6xl font-bold mb-4 tracking-tight">
|
||||
<span class="bg-gradient-to-r from-white to-blue-200 bg-clip-text text-transparent">
|
||||
Energiemonitoring
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-xl md:text-2xl text-blue-100 max-w-3xl mx-auto leading-relaxed">
|
||||
Überwachung des Energieverbrauchs aller 3D-Drucker mit TP-Link Tapo Smart Plugs
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
|
||||
{% if stats.error %}
|
||||
<!-- Error State -->
|
||||
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl p-6 mb-8">
|
||||
<div class="flex items-center">
|
||||
<svg class="w-6 h-6 text-red-500 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.464 0L4.35 16.5c-.77.833.192 2.5 1.732 2.5z"/>
|
||||
</svg>
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-red-800 dark:text-red-200">Energiemonitoring nicht verfügbar</h3>
|
||||
<p class="text-red-600 dark:text-red-300 mt-1">{{ stats.error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<!-- Energy Overview Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
|
||||
<!-- Total Power Consumption -->
|
||||
<div class="energy-card p-6">
|
||||
<div class="energy-metric">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-wider mb-2">Gesamtverbrauch</h3>
|
||||
<p class="text-3xl font-bold" id="total-power">{{ stats.total_power_consumption or '0.0' }} kWh</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Power -->
|
||||
<div class="energy-card p-6">
|
||||
<div class="energy-metric">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-wider mb-2">Aktuelle Leistung</h3>
|
||||
<p class="text-3xl font-bold" id="current-power">{{ stats.current_power or '0.0' }} W</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Active Devices -->
|
||||
<div class="energy-card p-6">
|
||||
<div class="energy-metric">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-wider mb-2">Aktive Geräte</h3>
|
||||
<p class="text-3xl font-bold" id="active-devices">{{ stats.active_devices or '0' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Energy Cost -->
|
||||
<div class="energy-card p-6">
|
||||
<div class="energy-metric">
|
||||
<h3 class="text-sm font-semibold uppercase tracking-wider mb-2">Kosten (€/kWh 0.30)</h3>
|
||||
<p class="text-3xl font-bold" id="energy-cost">{{ "%.2f"|format((stats.total_power_consumption or 0) * 0.30) }} €</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts Section -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||
|
||||
<!-- Power Consumption Over Time -->
|
||||
<div class="energy-card p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-4">Verbrauch der letzten 24 Stunden</h3>
|
||||
<div class="relative h-80">
|
||||
<canvas id="powerChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device Comparison -->
|
||||
<div class="energy-card p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-4">Verbrauch nach Gerät</h3>
|
||||
<div class="relative h-80">
|
||||
<canvas id="deviceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device List -->
|
||||
<div class="energy-card p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-6">Geräteübersicht</h3>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Gerät</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Aktuelle Leistung</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Tagesverbrauch</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="device-table-body" class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<!-- Dynamisch geladen via JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript für Live-Updates und Charts -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Live-Zeit aktualisieren
|
||||
function updateLiveTime() {
|
||||
const timeElement = document.getElementById('live-time');
|
||||
if (timeElement) {
|
||||
timeElement.textContent = new Date().toLocaleTimeString('de-DE');
|
||||
}
|
||||
}
|
||||
|
||||
// Alle Sekunde aktualisieren
|
||||
setInterval(updateLiveTime, 1000);
|
||||
updateLiveTime();
|
||||
|
||||
// Charts initialisieren
|
||||
initializeCharts();
|
||||
|
||||
// Geräteliste laden
|
||||
loadDeviceList();
|
||||
|
||||
// Auto-Update alle 30 Sekunden
|
||||
setInterval(() => {
|
||||
loadEnergyData();
|
||||
loadDeviceList();
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
function initializeCharts() {
|
||||
// Power Chart
|
||||
const powerCtx = document.getElementById('powerChart');
|
||||
if (powerCtx) {
|
||||
new Chart(powerCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [], // Wird dynamisch geladen
|
||||
datasets: [{
|
||||
label: 'Leistung (W)',
|
||||
data: [],
|
||||
borderColor: '#0073ce',
|
||||
backgroundColor: 'rgba(0, 115, 206, 0.1)',
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Leistung (W)'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Zeit'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Device Chart
|
||||
const deviceCtx = document.getElementById('deviceChart');
|
||||
if (deviceCtx) {
|
||||
new Chart(deviceCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: [], // Wird dynamisch geladen
|
||||
datasets: [{
|
||||
data: [],
|
||||
backgroundColor: [
|
||||
'#0073ce',
|
||||
'#005ba3',
|
||||
'#aaa9ad',
|
||||
'#5e5e5e',
|
||||
'#f59e0b',
|
||||
'#10b981'
|
||||
]
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function loadEnergyData() {
|
||||
try {
|
||||
const response = await fetch('/api/energy/overview');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
updateEnergyMetrics(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Energiedaten:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDeviceList() {
|
||||
try {
|
||||
const response = await fetch('/api/energy/devices');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
updateDeviceTable(data.devices || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Geräteliste:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateEnergyMetrics(data) {
|
||||
if (data.total_power_consumption !== undefined) {
|
||||
document.getElementById('total-power').textContent = data.total_power_consumption.toFixed(1) + ' kWh';
|
||||
}
|
||||
if (data.current_power !== undefined) {
|
||||
document.getElementById('current-power').textContent = data.current_power.toFixed(1) + ' W';
|
||||
}
|
||||
if (data.active_devices !== undefined) {
|
||||
document.getElementById('active-devices').textContent = data.active_devices;
|
||||
}
|
||||
if (data.total_power_consumption !== undefined) {
|
||||
const cost = (data.total_power_consumption * 0.30).toFixed(2);
|
||||
document.getElementById('energy-cost').textContent = cost + ' €';
|
||||
}
|
||||
}
|
||||
|
||||
function updateDeviceTable(devices) {
|
||||
const tbody = document.getElementById('device-table-body');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
if (devices.length === 0) {
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
|
||||
Keine Geräte gefunden
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
devices.forEach(device => {
|
||||
const row = document.createElement('tr');
|
||||
row.className = 'hover:bg-gray-50 dark:hover:bg-gray-800';
|
||||
|
||||
const statusClass = device.status === 'online' ? 'text-green-600' : 'text-red-600';
|
||||
const statusText = device.status === 'online' ? 'Online' : 'Offline';
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">${device.name}</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">${device.model || ''}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${statusClass}">
|
||||
${statusText}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
|
||||
${device.current_power || '0.0'} W
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white">
|
||||
${device.daily_consumption || '0.0'} kWh
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<a href="/energy/device/${device.id}" class="text-mercedes-blue hover:text-mercedes-blue/80">
|
||||
Details anzeigen
|
||||
</a>
|
||||
</td>
|
||||
`;
|
||||
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
321
backend/templates/energy_device_details.html
Normal file
321
backend/templates/energy_device_details.html
Normal file
@ -0,0 +1,321 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ device.name }} - Energiedetails - Mercedes-Benz TBA Marienfelde{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800">
|
||||
|
||||
<!-- Breadcrumb Navigation -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<nav class="flex" aria-label="Breadcrumb">
|
||||
<ol class="inline-flex items-center space-x-1 md:space-x-3">
|
||||
<li class="inline-flex items-center">
|
||||
<a href="/dashboard" class="text-gray-700 hover:text-mercedes-blue">
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="flex items-center">
|
||||
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<a href="/energy" class="ml-1 text-gray-700 hover:text-mercedes-blue">
|
||||
Energiemonitoring
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="flex items-center">
|
||||
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="ml-1 text-gray-500">{{ device.name }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Device Header -->
|
||||
<div class="relative overflow-hidden bg-gradient-to-r from-slate-900 via-blue-900 to-indigo-900 text-white rounded-3xl mx-4 mb-8">
|
||||
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div class="flex items-center space-x-6">
|
||||
<div class="w-16 h-16 bg-white/10 backdrop-blur-sm rounded-full flex items-center justify-center">
|
||||
<svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold">{{ device.name }}</h1>
|
||||
<p class="text-xl text-blue-200">{{ device.model }} - Energiedetails</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8">
|
||||
|
||||
<!-- Device Status Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
|
||||
<!-- Current Status -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Aktueller Status</h3>
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Status:</span>
|
||||
<span class="font-medium {% if device.status == 'online' %}text-green-600{% else %}text-red-600{% endif %}">
|
||||
{{ 'Online' if device.status == 'online' else 'Offline' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Aktuelle Leistung:</span>
|
||||
<span class="font-medium" id="current-power">{{ energy_data.current_power or '0.0' }} W</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Letzte Aktualisierung:</span>
|
||||
<span class="font-medium" id="last-update">{{ energy_data.last_update or 'Unbekannt' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Daily Stats -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Heute</h3>
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Verbrauch:</span>
|
||||
<span class="font-medium" id="daily-consumption">{{ energy_data.daily_consumption or '0.0' }} kWh</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Betriebszeit:</span>
|
||||
<span class="font-medium" id="daily-runtime">{{ energy_data.daily_runtime or '0h 0m' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Kosten:</span>
|
||||
<span class="font-medium" id="daily-cost">{{ "%.2f"|format((energy_data.daily_consumption or 0) * 0.30) }} €</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Monthly Stats -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Diesen Monat</h3>
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Verbrauch:</span>
|
||||
<span class="font-medium" id="monthly-consumption">{{ energy_data.monthly_consumption or '0.0' }} kWh</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Betriebszeit:</span>
|
||||
<span class="font-medium" id="monthly-runtime">{{ energy_data.monthly_runtime or '0h 0m' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Kosten:</span>
|
||||
<span class="font-medium" id="monthly-cost">{{ "%.2f"|format((energy_data.monthly_consumption or 0) * 0.30) }} €</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
<!-- Power Timeline -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-4">Leistungsverlauf (24h)</h3>
|
||||
<div class="relative h-80">
|
||||
<canvas id="powerTimelineChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Energy Consumption -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-4">Energieverbrauch (7 Tage)</h3>
|
||||
<div class="relative h-80">
|
||||
<canvas id="energyConsumptionChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Device Information -->
|
||||
<div class="mt-8 bg-white dark:bg-gray-800 rounded-xl shadow-md p-6">
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-6">Geräteinformationen</h3>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<h4 class="font-semibold text-gray-900 dark:text-white mb-3">Druckerdetails</h4>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Name:</span>
|
||||
<span class="font-medium">{{ device.name }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Modell:</span>
|
||||
<span class="font-medium">{{ device.model or 'Unbekannt' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Standort:</span>
|
||||
<span class="font-medium">{{ device.location or 'TBA Marienfelde' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">IP-Adresse:</span>
|
||||
<span class="font-medium">{{ device.ip_address or 'Nicht konfiguriert' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="font-semibold text-gray-900 dark:text-white mb-3">Smart Plug</h4>
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Plug IP:</span>
|
||||
<span class="font-medium">{{ device.plug_ip or 'Nicht konfiguriert' }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Verbindung:</span>
|
||||
<span class="font-medium {% if device.plug_ip %}text-green-600{% else %}text-red-600{% endif %}">
|
||||
{{ 'Konfiguriert' if device.plug_ip else 'Nicht konfiguriert' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600 dark:text-gray-400">Erstellungsdatum:</span>
|
||||
<span class="font-medium">{{ device.created_at.strftime('%d.%m.%Y') if device.created_at else 'Unbekannt' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeCharts();
|
||||
loadDeviceData();
|
||||
|
||||
// Auto-Update alle 30 Sekunden
|
||||
setInterval(loadDeviceData, 30000);
|
||||
});
|
||||
|
||||
function initializeCharts() {
|
||||
// Power Timeline Chart
|
||||
const powerCtx = document.getElementById('powerTimelineChart');
|
||||
if (powerCtx) {
|
||||
new Chart(powerCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Leistung (W)',
|
||||
data: [],
|
||||
borderColor: '#0073ce',
|
||||
backgroundColor: 'rgba(0, 115, 206, 0.1)',
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Leistung (W)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Energy Consumption Chart
|
||||
const energyCtx = document.getElementById('energyConsumptionChart');
|
||||
if (energyCtx) {
|
||||
new Chart(energyCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: 'Verbrauch (kWh)',
|
||||
data: [],
|
||||
backgroundColor: 'rgba(0, 115, 206, 0.8)',
|
||||
borderColor: '#0073ce',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Verbrauch (kWh)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDeviceData() {
|
||||
try {
|
||||
const deviceId = {{ device.id }};
|
||||
const response = await fetch(`/api/energy/device/${deviceId}`);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
updateDeviceMetrics(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Gerätedaten:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDeviceMetrics(data) {
|
||||
// Update current values
|
||||
if (data.current_power !== undefined) {
|
||||
document.getElementById('current-power').textContent = data.current_power.toFixed(1) + ' W';
|
||||
}
|
||||
|
||||
if (data.daily_consumption !== undefined) {
|
||||
document.getElementById('daily-consumption').textContent = data.daily_consumption.toFixed(2) + ' kWh';
|
||||
|
||||
// Update cost
|
||||
const dailyCost = (data.daily_consumption * 0.30).toFixed(2);
|
||||
document.getElementById('daily-cost').textContent = dailyCost + ' €';
|
||||
}
|
||||
|
||||
if (data.monthly_consumption !== undefined) {
|
||||
document.getElementById('monthly-consumption').textContent = data.monthly_consumption.toFixed(2) + ' kWh';
|
||||
|
||||
// Update cost
|
||||
const monthlyCost = (data.monthly_consumption * 0.30).toFixed(2);
|
||||
document.getElementById('monthly-cost').textContent = monthlyCost + ' €';
|
||||
}
|
||||
|
||||
if (data.daily_runtime !== undefined) {
|
||||
document.getElementById('daily-runtime').textContent = data.daily_runtime;
|
||||
}
|
||||
|
||||
if (data.monthly_runtime !== undefined) {
|
||||
document.getElementById('monthly-runtime').textContent = data.monthly_runtime;
|
||||
}
|
||||
|
||||
if (data.last_update !== undefined) {
|
||||
document.getElementById('last-update').textContent = new Date(data.last_update).toLocaleString('de-DE');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user