Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Off Topic
    4. IP-Scanner für das Hausnetz.

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    IP-Scanner für das Hausnetz.

    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      Beowolf last edited by

      Das ist ein kleiner Webserver-Scanner für das eigene Netzwerk. Das Programm listet dann die gefundenen Adressen auf und durch einen Mausklick kann die entsprechende Adresse direkt aufgerufen werden.

      Der IP-Bereich kann eingestellt werden.

      Ich habe mal versucht eine Anleitung für das Programm zu schreiben.

      1. Entpacke die ZIP-Datei.

      2. Installiere PyInstaller (falls noch nicht geschehen):

        pip install pyinstaller

      3. Starte das Build-Skript build_webscanner.bat per Doppelklick oder über die Eingabeaufforderung:

        build_webscanner.bat

      4. Die fertige EXE-Datei findest du danach im Ordner dist.

      webscanner.zip

      Hier der Inhalt der einzelnen Dateien. Nichts böses drin. 😊

      webscanner.py

      import tkinter as tk
      from tkinter import ttk
      import webbrowser
      import socket
      import threading
      import queue
      from concurrent.futures import ThreadPoolExecutor
      
      START_IP = "192.168.49.1"
      END_IP = "192.168.49.254"
      PORTS = [80]
      
      def ip_to_int(ip):
          parts = list(map(int, ip.split(".")))
          return (parts[0]<<24) + (parts[1]<<16) + (parts[2]<<8) + parts[3]
      
      def int_to_ip(i):
          return f"{(i>>24)&0xFF}.{(i>>16)&0xFF}.{(i>>8)&0xFF}.{i&0xFF}"
      
      def generate_ips(start_ip, end_ip):
          start = ip_to_int(start_ip)
          end = ip_to_int(end_ip)
          for i in range(start, end+1):
              yield int_to_ip(i)
      
      class WebserverScanner:
          def __init__(self, progress_callback=None):
              self.progress_callback = progress_callback
      
          def scan_ip_port(self, ip, port):
              try:
                  with socket.create_connection((ip, port), timeout=0.5):
                      return True
              except:
                  return False
      
          def get_hostname(self, ip):
              try:
                  return socket.gethostbyaddr(ip)[0]
              except:
                  return "Kein Hostname"
      
          def scan_threaded(self, ips, ports):
              results = []
              total = len(ips)*len(ports)
              count_lock = threading.Lock()
              count = [0]
      
              def scan_one(ip_port):
                  ip, port = ip_port
                  if self.scan_ip_port(ip, port):
                      hostname = self.get_hostname(ip)
                      res = {"ip": ip, "hostname": hostname, "port": port}
                  else:
                      res = None
                  with count_lock:
                      count[0] += 1
                      if self.progress_callback:
                          self.progress_callback(count[0], total)
                  return res
      
              ip_ports = [(ip, port) for ip in ips for port in ports]
      
              with ThreadPoolExecutor(max_workers=100) as executor:
                  for r in executor.map(scan_one, ip_ports):
                      if r:
                          results.append(r)
      
              return results
      
      class WebserverViewerApp:
          def __init__(self, root):
              self.root = root
              self.root.title("Webserver Scanner 192.168.49.x")
              self.root.geometry("900x700")
      
              self.entries = []
              self.queue = queue.Queue()
              self.resize_after_id = None
              self.current_columns = None  # Merke letzte Spaltenanzahl
              self.entries_displayed = None
      
              self.header = tk.Label(root, text="Webserver Scanner 192.168.49.1 - 254", font=("Arial", 18))
              self.header.pack(pady=10)
      
              self.progress = ttk.Progressbar(root, orient="horizontal", length=600, mode="determinate")
              self.progress.pack(pady=10)
      
              self.status_label = tk.Label(root, text="Bereit", font=("Arial", 12))
              self.status_label.pack()
      
              self.update_btn = tk.Button(root, text="Jetzt scannen", command=self.start_scan)
              self.update_btn.pack(pady=10)
      
              self.canvas = tk.Canvas(root)
              self.scroll_frame = tk.Frame(self.canvas)
              self.scrollbar = tk.Scrollbar(root, orient="vertical", command=self.canvas.yview)
              self.canvas.configure(yscrollcommand=self.scrollbar.set)
      
              self.scrollbar.pack(side="right", fill="y")
              self.canvas.pack(fill="both", expand=True)
      
              # Erstelle Fenster-Item für scroll_frame im Canvas
              self.canvas_frame = self.canvas.create_window((0,0), window=self.scroll_frame, anchor="nw")
      
              # Scrollregion aktualisieren bei Größe des scroll_frame
              self.scroll_frame.bind("<Configure>", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")))
      
              # Breite des scroll_frame immer an Canvas-Breite anpassen
              self.canvas.bind('<Configure>', self.resize_scroll_frame)
      
              # Debounced Resize-Handler für Fenstergröße
              self.root.bind("<Configure>", self.on_resize)
      
              self.root.after(100, self.check_queue)
      
          def resize_scroll_frame(self, event):
              self.canvas.itemconfig(self.canvas_frame, width=event.width)
      
          def on_resize(self, event):
              # Debounce mit 300ms Delay
              if self.resize_after_id:
                  self.root.after_cancel(self.resize_after_id)
              self.resize_after_id = self.root.after(300, lambda: self.update_display(self.entries))
      
          def start_scan(self):
              self.update_btn.config(state="disabled")
              self.progress['value'] = 0
              self.status_label.config(text="Scan läuft...")
              threading.Thread(target=self.run_scan, daemon=True).start()
      
          def progress_callback(self, count, total):
              percent = int((count / total) * 100)
              self.queue.put(("progress", percent))
      
          def run_scan(self):
              scanner = WebserverScanner(progress_callback=self.progress_callback)
              ips = list(generate_ips(START_IP, END_IP))
              results = scanner.scan_threaded(ips, PORTS)
              self.queue.put(("done", results))
      
          def check_queue(self):
              try:
                  while True:
                      msg, data = self.queue.get_nowait()
                      if msg == "progress":
                          self.progress['value'] = data
                          self.status_label.config(text=f"Scan läuft... {data}%")
                      elif msg == "done":
                          self.entries = data
                          self.update_display(data)
                          self.status_label.config(text=f"Scan abgeschlossen. {len(data)} Server gefunden.")
                          self.update_btn.config(state="normal")
              except queue.Empty:
                  pass
              self.root.after(100, self.check_queue)
      
          def update_display(self, entries):
              # Berechne aktuelle Spaltenanzahl
              frame_width = self.canvas.winfo_width() or 900
              box_min_width = 170
              columns = max(1, frame_width // box_min_width)
      
              # Prüfe, ob Spaltenanzahl oder Einträge sich geändert haben
              if self.current_columns == columns and self.entries_displayed == entries:
                  return
      
              self.current_columns = columns
              self.entries_displayed = entries
      
              # Alte Widgets löschen
              for widget in self.scroll_frame.winfo_children():
                  widget.destroy()
      
              if not entries:
                  tk.Label(self.scroll_frame, text="Keine Webserver gefunden", font=("Arial", 14), fg="red").pack(pady=20)
                  return
      
              box_height = 110
              for index, entry in enumerate(entries):
                  row = index // columns
                  col = index % columns
      
                  container = tk.Frame(self.scroll_frame, bd=1, relief="solid", padx=5, pady=5)
                  container.grid(row=row, column=col, padx=5, pady=5, sticky="nsew")
      
                  ip_label = tk.Label(container, text=entry['ip'], font=("Arial", 12, "bold"))
                  ip_label.pack()
      
                  if entry['hostname']:
                      host_label = tk.Label(container, text=entry['hostname'], font=("Arial", 10), fg="gray")
                      host_label.pack()
      
                  btn_color = "#4CAF50" if entry['port'] == 80 else "#2196F3"
                  open_button = tk.Button(container, text=f"Öffnen (Port {entry['port']})",
                                          bg=btn_color, fg="white",
                                          command=lambda ip=entry['ip'], port=entry['port']: self.open_url(ip, port))
                  open_button.pack(pady=5, fill="x")
      
              # Spalten gleichmäßig verteilen
              for c in range(columns):
                  self.scroll_frame.grid_columnconfigure(c, weight=1)
      
          def open_url(self, ip, port):
              url = f"https://{ip}" if port == 443 else f"http://{ip}"
              webbrowser.open(url)
      
      if __name__ == "__main__":
          root = tk.Tk()
          app = WebserverViewerApp(root)
          root.mainloop()
      
      

      build_webscanner.bat

      @echo off
      echo.
      echo ===============================
      echo   Building webscanner.exe ...
      echo ===============================
      echo.
      
      
      
      pyinstaller webscanner.spec
      
      echo.
      echo Fertig! EXE liegt in /dist/webscanner.exe
      pause
      

      webscanner.spec

      # webscanner.spec
      block_cipher = None
      
      a = Analysis(
          ['webscanner.py'],
          pathex=[],
          binaries=[],
          datas=[],
          hiddenimports=[],
          hookspath=[],
          hooksconfig={},
          runtime_hooks=[],
          excludes=[],
          win_no_prefer_redirects=False,
          win_private_assemblies=False,
          cipher=block_cipher,
          noarchive=False,
      )
      
      pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
      
      exe = EXE(
          pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='webscanner',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=False,  # GUI-Anwendung (kein Terminalfenster)
          )
      
      coll = COLLECT(
          exe,
          a.binaries,
          a.zipfiles,
          a.datas,
          strip=False,
          upx=True,
          name='webscanner',
      )
      

      Das Programm kann natürlich auch direkt im cmd-Fenster (Windows) durch python webscanner.py im entsprechenden Verzeichnis gestartet werden.

      Vor dem Start oder der Erstellung der EXE-Datei muß natürlich der IP-Bereich in webscanner.py angepasst werden.

      START_IP = "192.168.49.1"
      END_IP = "192.168.49.254"
      PORTS = [80]
      

      Wenn es läuft, sollte es etwa so aussehen:

      webscanner.jpg

      Viel Spaß damit

      OliverIO 1 Reply Last reply Reply Quote 0
      • arteck
        arteck Developer Most Active last edited by

        macht der das nicht auch ..

        dd5bd92a-a2e6-4416-93d1-fe4d348a101e-grafik.png

        B 1 Reply Last reply Reply Quote 0
        • B
          Beowolf @arteck last edited by

          @arteck
          Mag sein. Mein kleines PRogramm läuft "auserhalb" von ioBroker.

          Und das tolle an meinem Programm ist, es ist vollkommen kostenlos. Es kostet keine 5,95 €.

          😳 😳 😳

          Homoran 1 Reply Last reply Reply Quote 1
          • Homoran
            Homoran Global Moderator Administrators @Beowolf last edited by

            @beowolf sagte in IP-Scanner für das Hausnetz.:

            Mein kleines PRogramm läuft "auserhalb" von ioBroker.

            Deswegen hab ich es auch nach offTopic verschoben!

            1 Reply Last reply Reply Quote 0
            • B
              Beowolf last edited by

              Ok. Danke.

              1 Reply Last reply Reply Quote 0
              • OliverIO
                OliverIO @Beowolf last edited by OliverIO

                @beowolf

                Danke für das Skript.
                Ich verwende dafür den static site generator pelican in Verbindung mit einem nginx Webserver.
                Für jedes Gerät was ich ansteuerbar haben möchte lege ich eine kleine Datei an mit tite, subtext und Kategorie an. Vorteil ist, da nicht jedes Gerät auf Port 80 eine Admin Oberfläche anbietet (Cups,portainer, iobroker ) das individuell zu bearbeiten
                Auf Basis eines templates wird dann meine link Webseite Life aktualisiert (pelican läuft als docker Container und überwacht ob sich in einem Verzeichnis was ändert und startet dann die Erzeugung neu.

                Den Webserver habe ich intern auf die Domain links gemapt. Meist reicht auch nur l oder li im Browser aus um die Seite aufzurufen

                IMG_0345.jpeg

                1 Reply Last reply Reply Quote 1
                • Jey Cee
                  Jey Cee Developer last edited by

                  Ich verwende in der Regel Angry IP Scanner wenn ich ein eigenständiges Programm brauche.

                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post

                  Support us

                  ioBroker
                  Community Adapters
                  Donate

                  863
                  Online

                  31.7k
                  Users

                  79.7k
                  Topics

                  1.3m
                  Posts

                  5
                  7
                  267
                  Loading More Posts
                  • Oldest to Newest
                  • Newest to Oldest
                  • Most Votes
                  Reply
                  • Reply as topic
                  Log in to reply
                  Community
                  Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                  The ioBroker Community 2014-2023
                  logo