📚 Improved documentation for TAPO issue resolution in backend/TAPO_PROBLEMBEHEBUNG.md
This commit is contained in:
608
install.py
Normal file
608
install.py
Normal file
@ -0,0 +1,608 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
MYP (Manage Your Printers) Installer
|
||||
=====================================
|
||||
|
||||
Universeller Installer für das MYP Druckerverwaltungssystem.
|
||||
Unterstützt Installation, Kiosk-Modus und Desktop-Icon-Erstellung.
|
||||
|
||||
Aufruf vom Stammverzeichnis aus:
|
||||
python install.py # Interaktive Installation
|
||||
python install.py --kiosk # Direktinstallation mit Kiosk-Modus
|
||||
python install.py --desktop-icon # Nur Desktop-Icon erstellen
|
||||
python install.py --help # Hilfe anzeigen
|
||||
|
||||
Autor: Till Tomczak
|
||||
Version: 2.0
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import argparse
|
||||
import shutil
|
||||
import stat
|
||||
from pathlib import Path
|
||||
|
||||
class MYPInstaller:
|
||||
"""
|
||||
Hauptinstaller-Klasse für das MYP System.
|
||||
|
||||
Verwaltet die komplette Installation inklusive:
|
||||
- Systemabhängigkeiten
|
||||
- Python-Packages
|
||||
- Kiosk-Modus-Konfiguration
|
||||
- Desktop-Integration
|
||||
- Service-Setup
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.root_dir = Path(__file__).parent
|
||||
self.backend_dir = self.root_dir / "backend"
|
||||
self.setup_dir = self.backend_dir / "setup"
|
||||
self.is_root = os.geteuid() == 0 if hasattr(os, 'geteuid') else False
|
||||
|
||||
# System-Erkennung
|
||||
self.is_raspberry_pi = self._detect_raspberry_pi()
|
||||
self.is_debian = self._detect_debian()
|
||||
|
||||
print(f"""
|
||||
{'='*70}
|
||||
🏭 MYP (MANAGE YOUR PRINTERS) INSTALLER
|
||||
{'='*70}
|
||||
|
||||
🎯 Mercedes-Benz Druckerverwaltungssystem
|
||||
🔧 Universal-Installer für alle Plattformen
|
||||
📍 Arbeitsverzeichnis: {self.root_dir}
|
||||
|
||||
🔍 SYSTEM-ERKENNUNG:
|
||||
• Raspberry Pi: {'✅ JA' if self.is_raspberry_pi else '❌ NEIN'}
|
||||
• Debian/Ubuntu: {'✅ JA' if self.is_debian else '❌ NEIN'}
|
||||
• Root-Rechte: {'✅ JA' if self.is_root else '❌ NEIN'}
|
||||
|
||||
{'='*70}
|
||||
""")
|
||||
|
||||
def _detect_raspberry_pi(self):
|
||||
"""Erkennt Raspberry Pi Hardware"""
|
||||
try:
|
||||
with open('/proc/cpuinfo', 'r') as f:
|
||||
cpuinfo = f.read()
|
||||
return 'raspberry pi' in cpuinfo.lower() or 'bcm' in cpuinfo.lower()
|
||||
except:
|
||||
return False
|
||||
|
||||
def _detect_debian(self):
|
||||
"""Erkennt Debian-basierte Systeme"""
|
||||
try:
|
||||
return os.path.exists('/etc/debian_version')
|
||||
except:
|
||||
return False
|
||||
|
||||
def check_prerequisites(self):
|
||||
"""
|
||||
Prüft Systemvoraussetzungen für die Installation.
|
||||
|
||||
Returns:
|
||||
bool: True wenn alle Voraussetzungen erfüllt sind
|
||||
"""
|
||||
print("🔍 VORAUSSETZUNGSPRÜFUNG")
|
||||
print("-" * 40)
|
||||
|
||||
checks = []
|
||||
|
||||
# Python-Version
|
||||
if sys.version_info >= (3, 8):
|
||||
checks.append("✅ Python 3.8+ verfügbar")
|
||||
else:
|
||||
checks.append("❌ Python 3.8+ erforderlich")
|
||||
return False
|
||||
|
||||
# Git verfügbar
|
||||
try:
|
||||
subprocess.run(['git', '--version'], capture_output=True, check=True)
|
||||
checks.append("✅ Git verfügbar")
|
||||
except:
|
||||
checks.append("❌ Git nicht gefunden")
|
||||
|
||||
# Projekt-Struktur
|
||||
required_paths = [
|
||||
self.backend_dir,
|
||||
self.backend_dir / "app.py",
|
||||
self.backend_dir / "requirements.txt",
|
||||
self.setup_dir
|
||||
]
|
||||
|
||||
for path in required_paths:
|
||||
if path.exists():
|
||||
checks.append(f"✅ {path.name}")
|
||||
else:
|
||||
checks.append(f"❌ {path.name} fehlt")
|
||||
return False
|
||||
|
||||
# Systemtools (nur auf Linux)
|
||||
if self.is_debian:
|
||||
tools = ['systemctl', 'nginx', 'ufw']
|
||||
for tool in tools:
|
||||
try:
|
||||
subprocess.run(['which', tool], capture_output=True, check=True)
|
||||
checks.append(f"✅ {tool} verfügbar")
|
||||
except:
|
||||
checks.append(f"⚠️ {tool} nicht gefunden (wird installiert)")
|
||||
|
||||
for check in checks:
|
||||
print(f" {check}")
|
||||
|
||||
print("✅ Voraussetzungen erfüllt\n")
|
||||
return True
|
||||
|
||||
def install_system_dependencies(self):
|
||||
"""
|
||||
Installiert System-Abhängigkeiten über den Package-Manager.
|
||||
"""
|
||||
if not self.is_debian:
|
||||
print("⚠️ Überspringe System-Dependencies (nicht Debian-basiert)")
|
||||
return True
|
||||
|
||||
print("📦 SYSTEM-ABHÄNGIGKEITEN INSTALLIEREN")
|
||||
print("-" * 40)
|
||||
|
||||
# Basis-Pakete
|
||||
packages = [
|
||||
'python3-pip',
|
||||
'python3-venv',
|
||||
'nodejs',
|
||||
'npm',
|
||||
'nginx',
|
||||
'ufw',
|
||||
'sqlite3',
|
||||
'git',
|
||||
'curl',
|
||||
'wget',
|
||||
'unzip'
|
||||
]
|
||||
|
||||
# Raspberry Pi spezifische Pakete
|
||||
if self.is_raspberry_pi:
|
||||
packages.extend([
|
||||
'chromium-browser',
|
||||
'xorg',
|
||||
'openbox',
|
||||
'lightdm',
|
||||
'python3-gpiozero' # Für Hardware-Integration
|
||||
])
|
||||
|
||||
try:
|
||||
print(" Aktualisiere Package-Listen...")
|
||||
subprocess.run(['sudo', 'apt', 'update'], check=True)
|
||||
|
||||
print(f" Installiere {len(packages)} Pakete...")
|
||||
subprocess.run(['sudo', 'apt', 'install', '-y'] + packages, check=True)
|
||||
|
||||
print("✅ System-Abhängigkeiten installiert\n")
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Fehler bei System-Installation: {e}")
|
||||
return False
|
||||
|
||||
def install_python_dependencies(self):
|
||||
"""
|
||||
Installiert Python-Dependencies aus requirements.txt.
|
||||
"""
|
||||
print("🐍 PYTHON-ABHÄNGIGKEITEN INSTALLIEREN")
|
||||
print("-" * 40)
|
||||
|
||||
requirements_file = self.backend_dir / "requirements.txt"
|
||||
|
||||
try:
|
||||
# Virtual Environment erstellen (optional, aber empfohlen)
|
||||
venv_path = self.backend_dir / "venv"
|
||||
if not venv_path.exists():
|
||||
print(" Erstelle Virtual Environment...")
|
||||
subprocess.run([sys.executable, '-m', 'venv', str(venv_path)], check=True)
|
||||
|
||||
# Requirements installieren
|
||||
print(" Installiere Python-Packages...")
|
||||
if self.is_debian:
|
||||
# Auf Debian: --break-system-packages für pip
|
||||
subprocess.run([
|
||||
sys.executable, '-m', 'pip', 'install', '-r', str(requirements_file),
|
||||
'--break-system-packages'
|
||||
], check=True, cwd=self.backend_dir)
|
||||
else:
|
||||
subprocess.run([
|
||||
sys.executable, '-m', 'pip', 'install', '-r', str(requirements_file)
|
||||
], check=True, cwd=self.backend_dir)
|
||||
|
||||
print("✅ Python-Dependencies installiert\n")
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Fehler bei Python-Installation: {e}")
|
||||
return False
|
||||
|
||||
def build_frontend_assets(self):
|
||||
"""
|
||||
Baut Frontend-Assets (CSS, JavaScript).
|
||||
"""
|
||||
print("🎨 FRONTEND-ASSETS BAUEN")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
# npm-Dependencies installieren
|
||||
print(" Installiere npm-Dependencies...")
|
||||
subprocess.run(['npm', 'install'], check=True, cwd=self.backend_dir)
|
||||
|
||||
# TailwindCSS bauen
|
||||
print(" Baue TailwindCSS...")
|
||||
subprocess.run(['npm', 'run', 'build'], check=True, cwd=self.backend_dir)
|
||||
|
||||
print("✅ Frontend-Assets gebaut\n")
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Fehler beim Asset-Build: {e}")
|
||||
return False
|
||||
|
||||
def setup_database(self):
|
||||
"""
|
||||
Initialisiert die SQLite-Datenbank.
|
||||
"""
|
||||
print("🗄️ DATENBANK INITIALISIEREN")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
# Instance-Verzeichnis erstellen
|
||||
instance_dir = self.backend_dir / "instance"
|
||||
instance_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Datenbank initialisieren
|
||||
print(" Initialisiere SQLite-Datenbank...")
|
||||
subprocess.run([
|
||||
sys.executable, '-c',
|
||||
'from models import init_database; init_database()'
|
||||
], check=True, cwd=self.backend_dir)
|
||||
|
||||
print("✅ Datenbank initialisiert\n")
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Fehler bei Datenbank-Setup: {e}")
|
||||
return False
|
||||
|
||||
def setup_kiosk_mode(self):
|
||||
"""
|
||||
Konfiguriert Kiosk-Modus für Raspberry Pi.
|
||||
"""
|
||||
if not self.is_raspberry_pi:
|
||||
print("⚠️ Kiosk-Modus nur auf Raspberry Pi verfügbar")
|
||||
return True
|
||||
|
||||
print("🖥️ KIOSK-MODUS KONFIGURIEREN")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
# Kiosk-Skript erstellen
|
||||
kiosk_script = Path("/home/pi/kiosk.sh")
|
||||
kiosk_content = """#!/bin/bash
|
||||
|
||||
# MYP Kiosk-Modus Startskript
|
||||
# Startet Chromium im Vollbild-Modus
|
||||
|
||||
# Warten auf Netzwerk
|
||||
sleep 10
|
||||
|
||||
# Bildschirmschoner deaktivieren
|
||||
xset s off
|
||||
xset -dpms
|
||||
xset s noblank
|
||||
|
||||
# Chromium im Kiosk-Modus starten
|
||||
chromium-browser \\
|
||||
--no-sandbox \\
|
||||
--disable-infobars \\
|
||||
--disable-restore-session-state \\
|
||||
--disable-session-crashed-bubble \\
|
||||
--disable-features=TranslateUI \\
|
||||
--kiosk \\
|
||||
--app=https://localhost/
|
||||
"""
|
||||
|
||||
with open(kiosk_script, 'w') as f:
|
||||
f.write(kiosk_content)
|
||||
|
||||
# Ausführbar machen
|
||||
kiosk_script.chmod(stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH)
|
||||
|
||||
# Autostart konfigurieren
|
||||
autostart_dir = Path("/home/pi/.config/autostart")
|
||||
autostart_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
desktop_entry = autostart_dir / "myp-kiosk.desktop"
|
||||
desktop_content = f"""[Desktop Entry]
|
||||
Type=Application
|
||||
Name=MYP Kiosk
|
||||
Exec={kiosk_script}
|
||||
Hidden=false
|
||||
NoDisplay=false
|
||||
X-GNOME-Autostart-enabled=true
|
||||
"""
|
||||
|
||||
with open(desktop_entry, 'w') as f:
|
||||
f.write(desktop_content)
|
||||
|
||||
print(" ✅ Kiosk-Skript erstellt")
|
||||
print(" ✅ Autostart konfiguriert")
|
||||
print("✅ Kiosk-Modus konfiguriert\n")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler bei Kiosk-Setup: {e}")
|
||||
return False
|
||||
|
||||
def create_desktop_icon(self):
|
||||
"""
|
||||
Erstellt Desktop-Icon für MYP.
|
||||
"""
|
||||
print("🖱️ DESKTOP-ICON ERSTELLEN")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
# Desktop-Verzeichnis finden
|
||||
desktop_dirs = [
|
||||
Path.home() / "Desktop",
|
||||
Path.home() / "Schreibtisch",
|
||||
Path("/home/pi/Desktop")
|
||||
]
|
||||
|
||||
desktop_dir = None
|
||||
for dir_path in desktop_dirs:
|
||||
if dir_path.exists():
|
||||
desktop_dir = dir_path
|
||||
break
|
||||
|
||||
if not desktop_dir:
|
||||
print("⚠️ Desktop-Verzeichnis nicht gefunden")
|
||||
return True
|
||||
|
||||
# Icon-Datei kopieren
|
||||
icon_source = self.backend_dir / "static" / "favicon.svg"
|
||||
icon_dest = desktop_dir / "myp-icon.svg"
|
||||
|
||||
if icon_source.exists():
|
||||
shutil.copy2(icon_source, icon_dest)
|
||||
|
||||
# Desktop-Entry erstellen
|
||||
desktop_file = desktop_dir / "MYP-Druckerverwaltung.desktop"
|
||||
desktop_content = f"""[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=MYP Druckerverwaltung
|
||||
Comment=Mercedes-Benz Druckerverwaltungssystem
|
||||
Icon={icon_dest}
|
||||
Exec=python3 {self.backend_dir}/app.py
|
||||
Terminal=false
|
||||
Categories=Application;Office;
|
||||
StartupNotify=true
|
||||
"""
|
||||
|
||||
with open(desktop_file, 'w') as f:
|
||||
f.write(desktop_content)
|
||||
|
||||
# Ausführbar machen
|
||||
desktop_file.chmod(stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH)
|
||||
|
||||
print(f" ✅ Desktop-Icon erstellt: {desktop_file}")
|
||||
print("✅ Desktop-Integration abgeschlossen\n")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Fehler bei Desktop-Icon: {e}")
|
||||
return False
|
||||
|
||||
def setup_systemd_services(self):
|
||||
"""
|
||||
Installiert systemd-Services für automatischen Start.
|
||||
"""
|
||||
if not self.is_debian or not self.is_root:
|
||||
print("⚠️ Service-Setup erfordert Debian + Root-Rechte")
|
||||
return True
|
||||
|
||||
print("⚙️ SYSTEMD-SERVICES INSTALLIEREN")
|
||||
print("-" * 40)
|
||||
|
||||
try:
|
||||
# Service-Dateien kopieren
|
||||
systemd_dir = self.backend_dir / "systemd"
|
||||
if systemd_dir.exists():
|
||||
for service_file in systemd_dir.glob("*.service"):
|
||||
dest = Path("/etc/systemd/system") / service_file.name
|
||||
shutil.copy2(service_file, dest)
|
||||
print(f" ✅ {service_file.name} installiert")
|
||||
|
||||
# Services aktivieren
|
||||
services = ["myp-https.service"]
|
||||
if self.is_raspberry_pi:
|
||||
services.append("myp-kiosk.service")
|
||||
|
||||
for service in services:
|
||||
subprocess.run(['systemctl', 'daemon-reload'], check=True)
|
||||
subprocess.run(['systemctl', 'enable', service], check=True)
|
||||
print(f" ✅ {service} aktiviert")
|
||||
|
||||
print("✅ SystemD-Services installiert\n")
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"❌ Fehler bei Service-Setup: {e}")
|
||||
return False
|
||||
|
||||
def run_full_installation(self):
|
||||
"""
|
||||
Führt die komplette Installation durch.
|
||||
"""
|
||||
print("🚀 VOLLSTÄNDIGE INSTALLATION STARTEN")
|
||||
print("=" * 50)
|
||||
|
||||
steps = [
|
||||
("Voraussetzungen prüfen", self.check_prerequisites),
|
||||
("System-Abhängigkeiten", self.install_system_dependencies),
|
||||
("Python-Dependencies", self.install_python_dependencies),
|
||||
("Frontend-Assets", self.build_frontend_assets),
|
||||
("Datenbank", self.setup_database),
|
||||
("SystemD-Services", self.setup_systemd_services)
|
||||
]
|
||||
|
||||
for step_name, step_func in steps:
|
||||
print(f"\n📋 SCHRITT: {step_name}")
|
||||
if not step_func():
|
||||
print(f"\n❌ INSTALLATION FEHLGESCHLAGEN bei: {step_name}")
|
||||
return False
|
||||
|
||||
print(f"""
|
||||
{'='*70}
|
||||
🎉 INSTALLATION ERFOLGREICH ABGESCHLOSSEN!
|
||||
{'='*70}
|
||||
|
||||
✅ MYP Druckerverwaltungssystem wurde installiert
|
||||
|
||||
🔧 NÄCHSTE SCHRITTE:
|
||||
1. System neustarten (empfohlen)
|
||||
2. MYP-Service starten: sudo systemctl start myp-https
|
||||
3. Browser öffnen: https://localhost/
|
||||
|
||||
📝 WICHTIGE HINWEISE:
|
||||
• Standard-Admin: admin / admin123
|
||||
• Logs: {self.backend_dir}/logs/
|
||||
• Konfiguration: {self.backend_dir}/config/
|
||||
|
||||
🎯 MERCEDES-BENZ DRUCKERVERWALTUNG BEREIT!
|
||||
{'='*70}
|
||||
""")
|
||||
return True
|
||||
|
||||
def run_kiosk_installation(self):
|
||||
"""
|
||||
Führt Installation mit Kiosk-Modus durch.
|
||||
"""
|
||||
print("🖥️ KIOSK-INSTALLATION STARTEN")
|
||||
print("=" * 40)
|
||||
|
||||
# Basis-Installation
|
||||
if not self.run_full_installation():
|
||||
return False
|
||||
|
||||
# Kiosk-Modus konfigurieren
|
||||
if not self.setup_kiosk_mode():
|
||||
return False
|
||||
|
||||
print(f"""
|
||||
🎉 KIOSK-INSTALLATION ABGESCHLOSSEN!
|
||||
|
||||
🖥️ Das System startet automatisch im Kiosk-Modus
|
||||
• Vollbild-Browser mit MYP
|
||||
• Automatischer Start nach Boot
|
||||
• Bildschirmschoner deaktiviert
|
||||
|
||||
🔄 Neustart erforderlich für Kiosk-Aktivierung
|
||||
""")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Haupt-Installer-Funktion mit Argument-Parsing"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="MYP Druckerverwaltungssystem Installer",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
BEISPIELE:
|
||||
python install.py # Interaktive Installation
|
||||
python install.py --kiosk # Installation mit Kiosk-Modus
|
||||
python install.py --desktop-icon # Nur Desktop-Icon erstellen
|
||||
python install.py --full --kiosk # Vollinstallation + Kiosk
|
||||
|
||||
SYSTEMANFORDERUNGEN:
|
||||
• Python 3.8+
|
||||
• Debian/Ubuntu (empfohlen)
|
||||
• Root-Rechte für System-Services
|
||||
• Raspberry Pi für Kiosk-Modus
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument('--full', action='store_true',
|
||||
help='Vollständige Installation durchführen')
|
||||
parser.add_argument('--kiosk', action='store_true',
|
||||
help='Kiosk-Modus aktivieren')
|
||||
parser.add_argument('--desktop-icon', action='store_true',
|
||||
help='Desktop-Icon erstellen')
|
||||
parser.add_argument('--no-deps', action='store_true',
|
||||
help='System-Dependencies überspringen')
|
||||
parser.add_argument('--force', action='store_true',
|
||||
help='Installation trotz Warnungen fortsetzen')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Installer initialisieren
|
||||
installer = MYPInstaller()
|
||||
|
||||
# Kein Argument = Interaktive Installation
|
||||
if not any([args.full, args.kiosk, args.desktop_icon]):
|
||||
print("""
|
||||
🤔 INSTALLATIONSART WÄHLEN:
|
||||
|
||||
1) Vollständige Installation (empfohlen)
|
||||
2) Installation mit Kiosk-Modus (Raspberry Pi)
|
||||
3) Nur Desktop-Icon erstellen
|
||||
4) Abbrechen
|
||||
|
||||
Ihre Wahl [1-4]: """, end="")
|
||||
|
||||
try:
|
||||
choice = input().strip()
|
||||
if choice == '1':
|
||||
args.full = True
|
||||
elif choice == '2':
|
||||
args.kiosk = True
|
||||
elif choice == '3':
|
||||
args.desktop_icon = True
|
||||
else:
|
||||
print("Installation abgebrochen.")
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
print("\nInstallation abgebrochen.")
|
||||
return
|
||||
|
||||
# Installation ausführen
|
||||
success = True
|
||||
|
||||
if args.desktop_icon:
|
||||
success = installer.create_desktop_icon()
|
||||
elif args.kiosk:
|
||||
success = installer.run_kiosk_installation()
|
||||
elif args.full:
|
||||
success = installer.run_full_installation()
|
||||
if success and installer.is_raspberry_pi:
|
||||
create_kiosk = input("\n🖥️ Kiosk-Modus aktivieren? [j/N]: ").lower().startswith('j')
|
||||
if create_kiosk:
|
||||
installer.setup_kiosk_mode()
|
||||
|
||||
# Ergebnis
|
||||
if success:
|
||||
print("\n🎉 Installation erfolgreich!")
|
||||
|
||||
# Desktop-Icon anbieten falls nicht bereits erstellt
|
||||
if not args.desktop_icon and not args.kiosk:
|
||||
create_icon = input("🖱️ Desktop-Icon erstellen? [j/N]: ").lower().startswith('j')
|
||||
if create_icon:
|
||||
installer.create_desktop_icon()
|
||||
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n❌ Installation fehlgeschlagen!")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user