Files
Projektarbeit-MYP/backend/templates/tapo_manual_control.html

365 lines
16 KiB
HTML

{% extends "base.html" %}
{% block title %}
Manuelle Tapo-Steuerung | MYP Platform
{% endblock %}
{% block page_heading %}
<div class="flex items-center space-x-4">
<div class="bg-gradient-to-br from-red-500 to-orange-600 p-3 rounded-xl shadow-lg">
<i class="fas fa-tools text-white text-2xl"></i>
</div>
<div>
<h1 class="text-3xl font-bold text-slate-800 dark:text-white">
Manuelle Tapo-Steuerung
</h1>
<p class="text-slate-600 dark:text-slate-300">
Direkte Kontrolle beliebiger Tapo-Steckdosen (Admin-Bereich)
</p>
</div>
</div>
{% endblock %}
{% block page_actions %}
<div class="flex flex-wrap gap-3">
<a href="{{ url_for('tapo.tapo_dashboard') }}"
class="btn-secondary flex items-center space-x-2">
<i class="fas fa-arrow-left"></i>
<span>Zurück zur Übersicht</span>
</a>
</div>
{% endblock %}
{% block content %}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<!-- Manuelle Steuerung -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold text-slate-800 dark:text-white flex items-center">
<i class="fas fa-hand-paper mr-3"></i>
Manuelle Steuerung
</h2>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('tapo.manual_control') }}">
<div class="space-y-6">
<!-- IP-Adresse -->
<div>
<label for="ip" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
<i class="fas fa-network-wired mr-2"></i>IP-Adresse *
</label>
<input type="text"
id="ip"
name="ip"
required
pattern="^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
class="w-full px-3 py-2 border border-slate-300 dark:border-slate-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-slate-800 text-slate-900 dark:text-white"
placeholder="192.168.1.100">
<p class="text-sm text-slate-500 dark:text-slate-400 mt-1">
IP-Adresse der Tapo-Steckdose eingeben
</p>
</div>
<!-- Aktion auswählen -->
<div>
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">
<i class="fas fa-cog mr-2"></i>Aktion auswählen *
</label>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<label class="relative">
<input type="radio" name="action" value="on" class="sr-only peer" required>
<div class="p-4 border-2 border-slate-300 dark:border-slate-600 rounded-lg cursor-pointer peer-checked:border-green-500 peer-checked:bg-green-50 dark:peer-checked:bg-green-900/20 transition-all">
<div class="text-center">
<i class="fas fa-power-off text-green-600 text-2xl mb-2"></i>
<div class="font-medium text-slate-800 dark:text-white">Einschalten</div>
</div>
</div>
</label>
<label class="relative">
<input type="radio" name="action" value="off" class="sr-only peer">
<div class="p-4 border-2 border-slate-300 dark:border-slate-600 rounded-lg cursor-pointer peer-checked:border-slate-500 peer-checked:bg-slate-50 dark:peer-checked:bg-slate-900/20 transition-all">
<div class="text-center">
<i class="fas fa-power-off text-slate-600 text-2xl mb-2"></i>
<div class="font-medium text-slate-800 dark:text-white">Ausschalten</div>
</div>
</div>
</label>
<label class="relative">
<input type="radio" name="action" value="status" class="sr-only peer">
<div class="p-4 border-2 border-slate-300 dark:border-slate-600 rounded-lg cursor-pointer peer-checked:border-blue-500 peer-checked:bg-blue-50 dark:peer-checked:bg-blue-900/20 transition-all">
<div class="text-center">
<i class="fas fa-info-circle text-blue-600 text-2xl mb-2"></i>
<div class="font-medium text-slate-800 dark:text-white">Status prüfen</div>
</div>
</div>
</label>
</div>
</div>
<!-- Submit Button -->
<div class="pt-4">
<button type="submit" class="w-full btn-primary">
<i class="fas fa-play mr-2"></i>
Aktion ausführen
</button>
</div>
</div>
</form>
</div>
</div>
<!-- Quick Actions -->
<div class="card">
<div class="card-header">
<h2 class="text-xl font-semibold text-slate-800 dark:text-white flex items-center">
<i class="fas fa-bolt mr-3"></i>
Schnellaktionen
</h2>
</div>
<div class="card-body">
<div class="space-y-4">
<!-- Alle ausschalten -->
<div class="p-4 border border-orange-300 dark:border-orange-700 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<div class="flex items-center justify-between">
<div>
<h3 class="font-medium text-slate-800 dark:text-white">
<i class="fas fa-power-off mr-2 text-orange-600"></i>
Alle Steckdosen ausschalten
</h3>
<p class="text-sm text-slate-600 dark:text-slate-400 mt-1">
Schaltet alle konfigurierten Tapo-Steckdosen aus
</p>
</div>
<button onclick="emergencyShutdown()"
class="px-4 py-2 bg-orange-600 hover:bg-orange-700 text-white rounded-lg transition-colors">
<i class="fas fa-power-off mr-1"></i>
Alle AUS
</button>
</div>
</div>
<!-- Verbindung testen -->
<div class="p-4 border border-blue-300 dark:border-blue-700 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<div class="flex items-center justify-between">
<div>
<h3 class="font-medium text-slate-800 dark:text-white">
<i class="fas fa-network-wired mr-2 text-blue-600"></i>
Verbindung testen
</h3>
<p class="text-sm text-slate-600 dark:text-slate-400 mt-1">
Testet die IP-Adresse im Eingabefeld
</p>
</div>
<button onclick="testManualConnection()"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
<i class="fas fa-search mr-1"></i>
Testen
</button>
</div>
</div>
<!-- Status aller prüfen -->
<div class="p-4 border border-green-300 dark:border-green-700 bg-green-50 dark:bg-green-900/20 rounded-lg">
<div class="flex items-center justify-between">
<div>
<h3 class="font-medium text-slate-800 dark:text-white">
<i class="fas fa-sync-alt mr-2 text-green-600"></i>
Status aller prüfen
</h3>
<p class="text-sm text-slate-600 dark:text-slate-400 mt-1">
Aktualisiert den Status aller Steckdosen
</p>
</div>
<button onclick="refreshAllOutlets()"
class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors">
<i class="fas fa-sync-alt mr-1"></i>
Aktualisieren
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Warnung Box -->
<div class="card mt-8">
<div class="card-body">
<div class="flex items-start space-x-4 p-4 border border-yellow-400 dark:border-yellow-600 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-triangle text-yellow-600 dark:text-yellow-400 text-xl"></i>
</div>
<div>
<h3 class="font-medium text-yellow-800 dark:text-yellow-200 mb-2">
Wichtige Hinweise zur manuellen Steuerung
</h3>
<ul class="text-sm text-yellow-700 dark:text-yellow-300 space-y-1 list-disc list-inside">
<li>Diese Funktion ist nur für Administratoren verfügbar</li>
<li>IP-Adressen müssen gültig und erreichbar sein</li>
<li>Steckdosen müssen mit den globalen Tapo-Anmeldedaten konfiguriert sein</li>
<li>Alle Aktionen werden protokolliert</li>
<li>Bei Problemen immer erst die Verbindung testen</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Loading Overlay -->
<div id="loading-overlay" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
<div class="bg-white dark:bg-slate-800 p-6 rounded-lg shadow-lg">
<div class="flex items-center space-x-3">
<i class="fas fa-spinner fa-spin text-blue-600 text-xl"></i>
<span class="text-slate-800 dark:text-white font-medium" id="loading-text">
Lädt...
</span>
</div>
</div>
</div>
<script>
// Utility-Funktionen
function showLoading(text = 'Lädt...') {
document.getElementById('loading-text').textContent = text;
document.getElementById('loading-overlay').classList.remove('hidden');
}
function hideLoading() {
document.getElementById('loading-overlay').classList.add('hidden');
}
// Schnellaktionen
async function emergencyShutdown() {
if (!confirm('Möchten Sie wirklich ALLE Tapo-Steckdosen ausschalten?\n\nDies kann alle angeschlossenen Geräte abschalten!')) {
return;
}
try {
showLoading('Schalte alle Steckdosen aus...');
const response = await fetch('/tapo/all-status');
const data = await response.json();
if (!data.success) {
throw new Error(data.error);
}
let successCount = 0;
let errorCount = 0;
// Alle verfügbaren Steckdosen ausschalten
for (const [ip, status] of Object.entries(data.outlets)) {
if (status.reachable) {
try {
const controlResponse = await fetch('/tapo/control', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token() }}'
},
body: JSON.stringify({ ip, action: 'off' })
});
const controlData = await controlResponse.json();
if (controlData.success) {
successCount++;
} else {
errorCount++;
}
} catch (error) {
errorCount++;
}
}
}
if (successCount > 0) {
showFlashMessage(`${successCount} Steckdosen erfolgreich ausgeschaltet${errorCount > 0 ? `, ${errorCount} Fehler` : ''}`, 'success');
} else {
showFlashMessage('❌ Keine Steckdosen konnten ausgeschaltet werden', 'error');
}
} catch (error) {
showFlashMessage('Fehler beim Notaus: ' + error.message, 'error');
} finally {
hideLoading();
}
}
async function testManualConnection() {
const ipInput = document.getElementById('ip');
const ip = ipInput.value.trim();
if (!ip) {
showFlashMessage('Bitte geben Sie eine IP-Adresse ein', 'error');
ipInput.focus();
return;
}
// IP-Validation
if (!/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip)) {
showFlashMessage('Ungültige IP-Adresse', 'error');
ipInput.focus();
return;
}
try {
showLoading(`Teste Verbindung zu ${ip}...`);
const response = await fetch(`/tapo/test/${ip}`, { method: 'POST' });
const data = await response.json();
if (data.success) {
const testResult = data.test_result;
if (testResult.success) {
showFlashMessage(`✅ Verbindung zu ${ip} erfolgreich!`, 'success');
} else {
showFlashMessage(`❌ Verbindung zu ${ip} fehlgeschlagen: ${testResult.error}`, 'error');
}
} else {
showFlashMessage(`Verbindungstest fehlgeschlagen: ${data.error}`, 'error');
}
} catch (error) {
showFlashMessage('Fehler beim Verbindungstest: ' + error.message, 'error');
} finally {
hideLoading();
}
}
async function refreshAllOutlets() {
try {
showLoading('Aktualisiere Status aller Steckdosen...');
const response = await fetch('/tapo/all-status');
const data = await response.json();
if (data.success) {
showFlashMessage(`✅ Status von ${data.count} Steckdosen aktualisiert`, 'success');
} else {
showFlashMessage('Fehler beim Status-Update: ' + data.error, 'error');
}
} catch (error) {
showFlashMessage('Fehler beim Status-Update: ' + error.message, 'error');
} finally {
hideLoading();
}
}
// IP-Eingabe Auto-Format
document.getElementById('ip').addEventListener('input', function(e) {
let value = e.target.value.replace(/[^0-9.]/g, '');
e.target.value = value;
});
// Enter-Taste für Test
document.getElementById('ip').addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
testManualConnection();
}
});
</script>
{% endblock %}