Files
infoscreen/simclient/simclient.py
2025-07-15 10:45:56 +00:00

143 lines
4.0 KiB
Python

# 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()