# WMT Device Configuration Tool # Reads/writes data/config.txt (INI format). # On save, bumps [meta] last_synced to NOW so app.py will push an # update-request to Server_Monitorizare_v2 on next startup. import os import time import socket import subprocess import configparser from datetime import datetime import FreeSimpleGUI as sg CONFIG_PATH = "./data/config.txt" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _get_os_version(): try: with open("/etc/os-release") as f: for line in f: if line.startswith("PRETTY_NAME="): return line.split("=", 1)[1].strip().strip('"') except Exception: pass return "unknown" def _get_local_ip(): try: h = socket.gethostname() return socket.gethostbyname(h) except Exception: return "127.0.0.1" def _read_config(): """Return a dict with all device-relevant fields from config.txt.""" p = configparser.ConfigParser() p.read(CONFIG_PATH) return { "device_name": p.get("device", "work_place", fallback="notconfig"), "hostname": p.get("device", "hostname", fallback=socket.gethostname()), "device_ip": p.get("device", "ip", fallback=_get_local_ip()), "location": p.get("device", "location", fallback=""), "card_presence": p.get("device", "card_presence", fallback="enable"), "info_reviewed_at":p.get("device", "info_reviewed_at", fallback="1970-01-01T00:00:00"), # preserve other sections verbatim "chrome_url": p.get("chrome", "chrome_url", fallback=""), "chrome_local_url": p.get("chrome", "chrome_local_url", fallback=""), "chrome_insecure_origin":p.get("chrome", "chrome_insecure_origin",fallback=""), "card_api_base_url": p.get("card_api", "base_url", fallback=""), "server_log_url": p.get("server", "log_url", fallback=""), "update_host": p.get("server", "update_host", fallback=""), "update_user": p.get("server", "update_user", fallback=""), "internet_check_host": p.get("server", "internet_check_host", fallback=""), "last_synced": p.get("meta", "last_synced", fallback="1970-01-01T00:00:00"), } def _write_config(cfg): """Write all fields back to config.txt and bump last_synced to NOW.""" p = configparser.ConfigParser() p.add_section("chrome") p.set("chrome", "chrome_url", cfg.get("chrome_url", "")) p.set("chrome", "chrome_local_url", cfg.get("chrome_local_url", "")) p.set("chrome", "chrome_insecure_origin",cfg.get("chrome_insecure_origin", "")) p.add_section("card_api") p.set("card_api", "base_url", cfg.get("card_api_base_url", "")) p.add_section("server") p.set("server", "log_url", cfg.get("server_log_url", "")) p.set("server", "update_host", cfg.get("update_host", "")) p.set("server", "update_user", cfg.get("update_user", "")) p.set("server", "internet_check_host", cfg.get("internet_check_host", "")) p.add_section("device") p.set("device", "work_place", cfg["device_name"]) p.set("device", "hostname", cfg["hostname"]) p.set("device", "ip", cfg["device_ip"]) p.set("device", "location", cfg.get("location", "")) p.set("device", "card_presence", cfg.get("card_presence", "enable")) p.set("device", "info_reviewed_at", cfg.get("info_reviewed_at", "1970-01-01T00:00:00")) # Bump last_synced → tells app.py local config is newer → push update request now_ts = datetime.now().isoformat(timespec="seconds") p.add_section("meta") p.set("meta", "last_synced", now_ts) os.makedirs("./data", exist_ok=True) with open(CONFIG_PATH, "w") as f: f.write("# WMT Application Configuration\n") f.write(f"# Last updated by config.py: {now_ts}\n\n") p.write(f) print(f"✓ config.txt saved (last_synced={now_ts})") def _set_printer(device_name): """Configure CUPS printer to match the device/masa name.""" try: import cups conn = cups.Connection() printers = conn.getPrinters() if device_name in printers: print(f"Printer '{device_name}' is already configured.") return print(f"Installing printer '{device_name}' ...") cmd = (f"sudo /usr/sbin/lpadmin -p {device_name} -E " f"-v usb://CITIZEN/CT-S310II?serial=00000000 -m CTS310II.ppd") os.popen(cmd) time.sleep(2) print(f"Printer '{device_name}' installed.") except Exception as e: print(f"Printer setup skipped: {e}") def _update_hostname(new_hostname): """Change system hostname if it differs from the current one.""" current = socket.gethostname() if current == new_hostname: print(f"Hostname unchanged: {new_hostname}") return print(f"Updating hostname: {current} → {new_hostname}") subprocess.run(["sudo", "hostnamectl", "set-hostname", new_hostname], check=False) print("Hostname updated. A reboot is recommended.") def _show_result(title, lines): """Simple modal popup.""" layout = [[sg.Text(line)] for line in lines] + [[sg.Button("OK")]] w = sg.Window(title, layout, modal=True) while True: ev, _ = w.read() if ev in (sg.WIN_CLOSED, "OK"): break w.close() # --------------------------------------------------------------------------- # Main GUI # --------------------------------------------------------------------------- def main(): cfg = _read_config() os_ver = _get_os_version() local_ip = _get_local_ip() sg.theme("Dark Grey 9") sg.set_options(font=("Arial Bold", 15)) LABEL_W = 22 INPUT_W = 38 layout = [ [sg.Text("WMT Device Configuration", font=("Arial Bold", 18), justification="center", expand_x=True, pad=(0, 12))], # ── Editable fields ────────────────────────────────────── [sg.HorizontalSeparator()], [sg.Text("── Device Identity ──", font=("Arial Bold", 13), text_color="#aaaaaa", pad=(0, 6))], [sg.Text("Hostname", size=(LABEL_W, 1)), sg.InputText(cfg["hostname"], key="-HOSTNAME-", size=(INPUT_W, 1))], [sg.Text("Loc de Munca / Masa", size=(LABEL_W, 1)), sg.InputText(cfg["device_name"], key="-MASA-", size=(INPUT_W, 1))], [sg.Text("Location", size=(LABEL_W, 1)), sg.InputText(cfg.get("location", ""), key="-LOCATION-", size=(INPUT_W, 1), tooltip="Physical location, e.g. Floor 2, Line A, Masa-01")], [sg.Text("Card Presence", size=(LABEL_W, 1)), sg.Combo(["enable", "disable"], default_value=cfg.get("card_presence", "enable"), key="-CARD_PRESENCE-", size=(INPUT_W - 2, 1), readonly=True, tooltip="enable = card reader active; disable = no card reader")], # ── Read-only system info ───────────────────────────────── [sg.HorizontalSeparator()], [sg.Text("── System Info (read-only) ──", font=("Arial Bold", 13), text_color="#aaaaaa", pad=(0, 6))], [sg.Text("OS Version", size=(LABEL_W, 1)), sg.InputText(os_ver, key="-OS-", size=(INPUT_W, 1), disabled=True, text_color="#888888")], [sg.Text("IP Address", size=(LABEL_W, 1)), sg.InputText(local_ip, key="-IP-", size=(INPUT_W, 1), disabled=True, text_color="#888888")], [sg.Text("Last Synced", size=(LABEL_W, 1)), sg.InputText(cfg["last_synced"], key="-SYNC-", size=(INPUT_W, 1), disabled=True, text_color="#888888")], # ── Status output ───────────────────────────────────────── [sg.HorizontalSeparator()], [sg.Multiline("", key="-OUT-", size=(62, 5), disabled=True, autoscroll=True, background_color="#1a1a1a", text_color="#cccccc", font=("Courier", 12))], # ── Buttons ─────────────────────────────────────────────── [sg.Button("Save & Apply", key="-SAVE-", button_color=("white", "#2980b9"), size=(18, 1)), sg.Button("Cancel", key="-CANCEL-", size=(12, 1))], ] window = sg.Window("WMT Configuration", layout, size=(720, 540), finalize=True) def log(msg): window["-OUT-"].update(disabled=False) window["-OUT-"].print(msg) window["-OUT-"].update(disabled=True) while True: event, values = window.read() if event in (sg.WIN_CLOSED, "-CANCEL-"): break if event == "-SAVE-": new_hostname = values["-HOSTNAME-"].strip() new_masa = values["-MASA-"].strip() new_location = values["-LOCATION-"].strip() new_card_presence = values["-CARD_PRESENCE-"] if not new_hostname or not new_masa: _show_result("Validation Error", ["Hostname and Loc de Munca / Masa are required."]) continue # Update cfg dict with edited values cfg["hostname"] = new_hostname cfg["device_name"] = new_masa cfg["location"] = new_location cfg["card_presence"] = new_card_presence cfg["device_ip"] = local_ip # 1. Save config.txt (bumps last_synced → triggers server update request) try: _write_config(cfg) log("✓ config.txt saved. last_synced updated to NOW.") log(" → app.py will push update request to server on next start.") except Exception as e: log(f"✗ Failed to save config.txt: {e}") # 2. Update hostname if changed try: _update_hostname(new_hostname) log(f"✓ Hostname: {new_hostname}") except Exception as e: log(f"✗ Hostname update error: {e}") # 3. Configure printer try: _set_printer(new_masa) log(f"✓ Printer check done for '{new_masa}'.") except Exception as e: log(f" Printer: {e}") log("") log("Done. Reboot recommended if hostname changed.") window.close() if __name__ == "__main__": main()