288 lines
11 KiB
Python
288 lines
11 KiB
Python
# 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|