326 lines
16 KiB
HTML
326 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}
|
|
Tapo-Steckdosen-Steuerung | MYP Platform
|
|
{% endblock %}
|
|
|
|
{% block page_heading %}
|
|
<div class="flex items-center space-x-4">
|
|
<div class="bg-gradient-to-br from-orange-500 to-red-600 p-3 rounded-xl shadow-lg">
|
|
<i class="fas fa-plug text-white text-2xl"></i>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-slate-800 dark:text-white">
|
|
Tapo-Steckdosen-Steuerung
|
|
</h1>
|
|
<p class="text-slate-600 dark:text-slate-300">
|
|
Mercedes-Benz TBA Marienfelde - 6 Arbeitsplätze für 3D-Drucker
|
|
</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-sync-alt"></i>
|
|
<span>Status aktualisieren</span>
|
|
</a>
|
|
|
|
{% if current_user.is_authenticated and current_user.has_permission('ADMIN') %}
|
|
<form method="POST" action="{{ url_for('tapo.discover_outlets') }}" class="inline">
|
|
<button type="submit" class="btn-primary flex items-center space-x-2">
|
|
<i class="fas fa-search"></i>
|
|
<span>Steckdosen suchen</span>
|
|
</button>
|
|
</form>
|
|
|
|
<a href="{{ url_for('tapo.manual_control') }}"
|
|
class="btn-outline flex items-center space-x-2">
|
|
<i class="fas fa-tools"></i>
|
|
<span>Manuelle Steuerung</span>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Statistik-Übersicht -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
|
<div class="card">
|
|
<div class="flex items-center space-x-4">
|
|
<div class="bg-blue-100 dark:bg-blue-900 p-3 rounded-lg">
|
|
<i class="fas fa-plug text-blue-600 dark:text-blue-300 text-xl"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-slate-700 dark:text-slate-300">
|
|
Arbeitsplätze Gesamt
|
|
</h3>
|
|
<p class="text-2xl font-bold text-slate-900 dark:text-white">
|
|
{{ total_outlets or 6 }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="flex items-center space-x-4">
|
|
<div class="bg-green-100 dark:bg-green-900 p-3 rounded-lg">
|
|
<i class="fas fa-wifi text-green-600 dark:text-green-300 text-xl"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-slate-700 dark:text-slate-300">
|
|
Online
|
|
</h3>
|
|
<p class="text-2xl font-bold text-slate-900 dark:text-white">
|
|
{{ online_outlets or 0 }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="flex items-center space-x-4">
|
|
<div class="bg-orange-100 dark:bg-orange-900 p-3 rounded-lg">
|
|
<i class="fas fa-bolt text-orange-600 dark:text-orange-300 text-xl"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-lg font-semibold text-slate-700 dark:text-slate-300">
|
|
Aktive
|
|
</h3>
|
|
<p class="text-2xl font-bold text-slate-900 dark:text-white">
|
|
{{ outlets.values() | selectattr('status', 'equalto', 'on') | list | length }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mercedes-Benz 6 Arbeitsplätze -->
|
|
<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-industry mr-3"></i>
|
|
Mercedes-Benz TBA Marienfelde - 3D-Drucker Arbeitsplätze
|
|
</h2>
|
|
<p class="text-sm text-slate-600 dark:text-slate-400 mt-2">
|
|
Feste Installation mit 6 konfigurierten Arbeitsplätzen
|
|
</p>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<!-- Steckdosen-Grid - Immer 6 Arbeitsplätze anzeigen -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="outlets-grid">
|
|
{% if outlets %}
|
|
{% for ip, outlet in outlets.items() %}
|
|
<div class="outlet-card border rounded-lg p-4 {{ 'border-green-300 bg-green-50 dark:bg-green-900/20' if outlet.reachable else 'border-red-300 bg-red-50 dark:bg-red-900/20' }}"
|
|
data-ip="{{ ip }}">
|
|
|
|
<!-- Arbeitsplatz-Header -->
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="status-indicator w-4 h-4 rounded-full {{ 'bg-green-500' if outlet.reachable else 'bg-red-500' }}"></div>
|
|
<div>
|
|
<h3 class="font-semibold text-slate-800 dark:text-white">
|
|
{{ outlet.printer_name }}
|
|
</h3>
|
|
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
IP: {{ ip }}
|
|
</p>
|
|
{% if outlet.model %}
|
|
<p class="text-xs text-slate-500 dark:text-slate-500">
|
|
{{ outlet.model }}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center space-x-2">
|
|
<!-- Konfigurationsstatus -->
|
|
{% if outlet.configured_in_db %}
|
|
<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded-full dark:bg-blue-900 dark:text-blue-300"
|
|
title="In Datenbank konfiguriert">
|
|
<i class="fas fa-database text-xs"></i>
|
|
</span>
|
|
{% else %}
|
|
<span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded-full dark:bg-yellow-900 dark:text-yellow-300"
|
|
title="Nicht in Datenbank konfiguriert">
|
|
<i class="fas fa-exclamation-triangle text-xs"></i>
|
|
</span>
|
|
{% endif %}
|
|
|
|
<!-- Position-Badge -->
|
|
<span class="bg-slate-100 text-slate-700 text-xs px-2 py-1 rounded-full dark:bg-slate-700 dark:text-slate-300">
|
|
#{{ outlet.position or loop.index }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status-Informationen -->
|
|
<div class="mb-4 space-y-2">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-slate-600 dark:text-slate-400">Status:</span>
|
|
<span class="status-text font-medium">
|
|
{% if outlet.reachable %}
|
|
{% if outlet.status == 'on' %}
|
|
<span class="text-green-600 dark:text-green-400">
|
|
<i class="fas fa-power-off mr-1"></i>EIN
|
|
</span>
|
|
{% elif outlet.status == 'off' %}
|
|
<span class="text-slate-600 dark:text-slate-400">
|
|
<i class="fas fa-power-off mr-1"></i>AUS
|
|
</span>
|
|
{% else %}
|
|
<span class="text-yellow-600 dark:text-yellow-400">
|
|
<i class="fas fa-question mr-1"></i>UNBEKANNT
|
|
</span>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="text-red-600 dark:text-red-400">
|
|
<i class="fas fa-exclamation-triangle mr-1"></i>OFFLINE
|
|
</span>
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-slate-600 dark:text-slate-400">Standort:</span>
|
|
<span class="text-sm font-medium text-slate-800 dark:text-white">
|
|
{{ outlet.location }}
|
|
</span>
|
|
</div>
|
|
|
|
{% if outlet.configured_in_db and outlet.printer_id %}
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-slate-600 dark:text-slate-400">Drucker-ID:</span>
|
|
<span class="text-sm font-medium text-slate-800 dark:text-white">
|
|
#{{ outlet.printer_id }}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Steuerungsformen (ohne JavaScript) -->
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<!-- EIN-Button -->
|
|
<form method="POST" action="{{ url_for('tapo.control_outlet_form') }}" class="inline">
|
|
<input type="hidden" name="ip" value="{{ ip }}">
|
|
<input type="hidden" name="action" value="on">
|
|
<button type="submit"
|
|
class="w-full py-2 px-3 bg-green-600 hover:bg-green-700 text-white rounded-lg transition-colors font-medium text-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
|
{% if not outlet.reachable %}disabled{% endif %}>
|
|
<i class="fas fa-power-off mr-1"></i>
|
|
EIN
|
|
</button>
|
|
</form>
|
|
|
|
<!-- AUS-Button -->
|
|
<form method="POST" action="{{ url_for('tapo.control_outlet_form') }}" class="inline">
|
|
<input type="hidden" name="ip" value="{{ ip }}">
|
|
<input type="hidden" name="action" value="off">
|
|
<button type="submit"
|
|
class="w-full py-2 px-3 bg-slate-600 hover:bg-slate-700 text-white rounded-lg transition-colors font-medium text-sm disabled:opacity-50 disabled:cursor-not-allowed"
|
|
{% if not outlet.reachable %}disabled{% endif %}>
|
|
<i class="fas fa-power-off mr-1"></i>
|
|
AUS
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
{% if outlet.error %}
|
|
<div class="mt-3 p-2 bg-red-100 dark:bg-red-900/30 border border-red-300 dark:border-red-700 rounded text-sm text-red-700 dark:text-red-300">
|
|
<i class="fas fa-exclamation-triangle mr-1"></i>
|
|
{{ outlet.error }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Zusätzliche Aktionen für Administratoren -->
|
|
{% if current_user.has_permission('ADMIN') %}
|
|
<div class="mt-3 pt-3 border-t border-slate-200 dark:border-slate-600">
|
|
<div class="flex space-x-2">
|
|
<form method="POST" action="{{ url_for('tapo.test_connection_form') }}" class="flex-1">
|
|
<input type="hidden" name="ip" value="{{ ip }}">
|
|
<button type="submit"
|
|
class="w-full text-xs py-1 px-2 text-slate-600 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-200 border border-slate-300 dark:border-slate-600 rounded transition-colors">
|
|
<i class="fas fa-network-wired mr-1"></i>Test
|
|
</button>
|
|
</form>
|
|
|
|
{% if not outlet.configured_in_db %}
|
|
<a href="{{ url_for('admin.add_printer') }}?preset_plug_ip={{ ip }}"
|
|
class="flex-1 text-xs py-1 px-2 text-center text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200 border border-blue-300 dark:border-blue-600 rounded transition-colors">
|
|
<i class="fas fa-plus mr-1"></i>Setup
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<!-- Fallback: Zeige 6 Platzhalter-Arbeitsplätze -->
|
|
{% for i in range(1, 7) %}
|
|
<div class="outlet-card border border-red-300 bg-red-50 dark:bg-red-900/20 rounded-lg p-4">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center space-x-3">
|
|
<div class="status-indicator w-4 h-4 rounded-full bg-red-500"></div>
|
|
<div>
|
|
<h3 class="font-semibold text-slate-800 dark:text-white">
|
|
3D-Drucker {{ i }}
|
|
</h3>
|
|
<p class="text-sm text-slate-600 dark:text-slate-400">
|
|
192.168.1.20{{ i }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<span class="bg-slate-100 text-slate-700 text-xs px-2 py-1 rounded-full dark:bg-slate-700 dark:text-slate-300">
|
|
#{{ i }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-sm text-slate-600 dark:text-slate-400">Status:</span>
|
|
<span class="text-red-600 dark:text-red-400">
|
|
<i class="fas fa-exclamation-triangle mr-1"></i>NICHT KONFIGURIERT
|
|
</span>
|
|
</div>
|
|
<div class="flex items-center justify-between mt-2">
|
|
<span class="text-sm text-slate-600 dark:text-slate-400">Standort:</span>
|
|
<span class="text-sm font-medium text-slate-800 dark:text-white">
|
|
Arbeitsplatz {{ i }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<button disabled class="w-full py-2 px-3 bg-slate-400 text-white rounded-lg opacity-50 cursor-not-allowed text-sm">
|
|
<i class="fas fa-power-off mr-1"></i>EIN
|
|
</button>
|
|
<button disabled class="w-full py-2 px-3 bg-slate-400 text-white rounded-lg opacity-50 cursor-not-allowed text-sm">
|
|
<i class="fas fa-power-off mr-1"></i>AUS
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-3 p-2 bg-yellow-100 dark:bg-yellow-900/30 border border-yellow-300 dark:border-yellow-700 rounded text-sm text-yellow-700 dark:text-yellow-300">
|
|
<i class="fas fa-info-circle mr-1"></i>
|
|
Arbeitsplatz {{ i }} noch nicht eingerichtet
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Auto-Refresh nur falls explizit gewünscht (minimales JavaScript) -->
|
|
{% if request.args.get('auto_refresh') %}
|
|
<script>
|
|
// Minimales Auto-Refresh alle 30 Sekunden
|
|
setTimeout(function() {
|
|
window.location.reload();
|
|
}, 30000);
|
|
</script>
|
|
{% endif %}
|
|
{% endblock %} |