Files
WMT/config.py

278 lines
10 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=""),
"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", "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")],
# ── 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()
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["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()