# simclient/simclient.py import time import uuid import json import socket import hashlib import paho.mqtt.client as mqtt import os import re import platform import logging DEBUG_MODE = True # Auf False setzen für Produktion # Logging-Konfiguration LOG_DIR = os.path.dirname(os.path.abspath(__file__)) LOG_PATH = os.path.join(LOG_DIR, "simclient.log") log_handlers = [logging.FileHandler(LOG_PATH, encoding="utf-8")] if DEBUG_MODE: log_handlers.append(logging.StreamHandler()) logging.basicConfig( level=logging.DEBUG if DEBUG_MODE else logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=log_handlers ) def on_message(client, userdata, msg): logging.info(f"Empfangen: {msg.topic} {msg.payload.decode()}") def get_mac_addresses(): macs = set() try: for root, dirs, files in os.walk('/sys/class/net/'): for iface in dirs: try: with open(f'/sys/class/net/{iface}/address') as f: mac = f.read().strip() if mac and mac != '00:00:00:00:00:00': macs.add(mac) except Exception: continue break except Exception: pass return sorted(macs) def get_board_serial(): # Raspberry Pi: /proc/cpuinfo, andere: /sys/class/dmi/id/product_serial serial = None try: with open('/proc/cpuinfo') as f: for line in f: if line.lower().startswith('serial'): serial = line.split(':')[1].strip() break except Exception: pass if not serial: try: with open('/sys/class/dmi/id/product_serial') as f: serial = f.read().strip() except Exception: pass return serial or "unknown" def get_ip(): # Versucht, die lokale IP zu ermitteln (nicht 127.0.0.1) try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = s.getsockname()[0] s.close() return ip except Exception: return "unknown" def get_hardware_token(): serial = get_board_serial() macs = get_mac_addresses() token_raw = serial + "_" + "_".join(macs) # Hashen für Datenschutz token_hash = hashlib.sha256(token_raw.encode()).hexdigest() return token_hash def get_model(): # Versucht, das Modell auszulesen (z.B. Raspberry Pi, PC, etc.) try: if os.path.exists('/proc/device-tree/model'): with open('/proc/device-tree/model') as f: return f.read().strip() elif os.path.exists('/sys/class/dmi/id/product_name'): with open('/sys/class/dmi/id/product_name') as f: return f.read().strip() except Exception: pass return "unknown" SOFTWARE_VERSION = "1.0.0" # Optional: Anpassen bei neuen Releases def send_discovery(client, client_id, hardware_token, ip_addr): macs = get_mac_addresses() discovery_msg = { "client_id": client_id, "hardware_token": hardware_token, "ip": ip_addr, "type": "infoscreen", "hostname": socket.gethostname(), "os_version": platform.platform(), "software_version": SOFTWARE_VERSION, "macs": macs, "model": get_model(), } client.publish("infoscreen/discovery", json.dumps(discovery_msg)) logging.info(f"Discovery-Nachricht gesendet: {discovery_msg}") def main(): client_id = str(uuid.uuid4()) hardware_token = get_hardware_token() ip_addr = get_ip() client = mqtt.Client(protocol=mqtt.MQTTv311, callback_api_version=2) client.on_message = on_message client.connect("mqtt", 1883) client.subscribe(f"infoscreen/{client_id}/config") send_discovery(client, client_id, hardware_token, ip_addr) while True: # Heartbeat senden client.publish(f"infoscreen/{client_id}/heartbeat", "alive") logging.debug("Heartbeat gesendet.") client.loop(timeout=1.0) time.sleep(5) if __name__ == "__main__": main()