NA: ALL AUTO-FIX key pauses or resumes auto-fix at every location; page keys keep their words under the band; rc4 ProP module at WS/SC
- LOCATIONS page key at 3,1: shows ALL ON or the paused locations; tap pauses all, tap again sends each back to its hours - autofix auto: back to the auto-fix hours - the page name and number shrink into the top 60% while the locations band shows - WS and SC Propresenter-API on renewedvision-propresenter-api 1.2.0-rc4 (pinned manual, as NA) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+69
-3
@@ -74,9 +74,9 @@ def me(cfg, url=None):
|
||||
"user": pwd.getpwuid(os.getuid()).pw_name}
|
||||
|
||||
|
||||
# Bumped when the pages are built differently (2: "campus" became "location", 2026-10-21), so the hub
|
||||
# Bumped when the pages are built differently (2: "campus" became "location"; 3: ALL AUTO-FIX key, 2026-10-21), so the hub
|
||||
# re-imports them.
|
||||
PAGES_VERSION = 2
|
||||
PAGES_VERSION = 3
|
||||
|
||||
|
||||
def layout_sha(cfg):
|
||||
@@ -105,6 +105,9 @@ def http_json(url, body=None, timeout=3):
|
||||
|
||||
VIEW = {"state": "sp_state_view", "heartbeat": "sp_hb_view"}
|
||||
REMOTE = make_page.REMOTE_VAR # which other locations have a problem: the blue band on NA's page-number keys
|
||||
ALL_CMD = "sp_all_cmd" # a tap on NA's ALL AUTO-FIX key (LOCATIONS page)
|
||||
ALL_AF = "sp_all_af" # what that key shows: {"t", "x", "paused"}
|
||||
ALL_AF_POS = (3, 1) # where AUTO-FIX sits on every location's own status page
|
||||
SELECT = "sp_view"
|
||||
VIEW_CMD = "sp_view_cmd" # a tap or hold on the LOCATION page, passed on to that location's own watchdog
|
||||
MODE = "sp_view_mode" # "" = that location's status page, "RESTART" = its Restart page
|
||||
@@ -199,14 +202,51 @@ def build_overview(local_cfg, campuses, page, view_page):
|
||||
make_page.place(controls, (0, col), key)
|
||||
if restart:
|
||||
make_page.place(controls, (1, col), restart)
|
||||
make_page.place(controls, ALL_AF_POS, all_autofix_key(local_cfg))
|
||||
return make_page.page_export("%d\\nLocations" % page, page, controls, local_cfg)
|
||||
|
||||
|
||||
def all_autofix_key(local_cfg):
|
||||
"""Tap: pause auto-fix at every location at once (NA too); tap again: every location back to its hours
|
||||
(2026-10-21, the user: "add a key on NA to pause auto-fix at all locations"). Blue while none is paused,
|
||||
grey naming the paused ones."""
|
||||
v = "$(custom:%s)" % ALL_AF
|
||||
stale = "$(internal:time_unix) - jsonpath(%s, '$.t') > 30" % v
|
||||
key = make_page.static_button("all_autofix", "AUTO-FIX\\nALL", 0x0B57D0, 0xFFFFFF, 1,
|
||||
"Tap: pause auto-fix at every location (alarms stay on, holds still fix); tap again: all "
|
||||
"back to their auto-fix hours.", "STATUS tile 'all autofix'")
|
||||
for layer in key["style"]["layers"]:
|
||||
if layer["type"] == "box":
|
||||
layer["color"] = make_page.prop("%s ? %d : (jsonpath(%s, '$.paused') > 0 ? %d : %d)" % (stale, make_page.PURPLE, v, 0x262626, 0x0B57D0), True)
|
||||
elif layer["type"] == "text":
|
||||
layer["text"] = make_page.prop("%s ? `AUTO-FIX\\nALL ?` : `AUTO-FIX\\n${jsonpath(%s, '$.x')}`" % (stale, v), True)
|
||||
key["steps"]["0"]["action_sets"]["up"] = [make_page.command_action("all_autofix", "press", ALL_CMD, "toggle $(internal:time_unix)")]
|
||||
return key
|
||||
|
||||
|
||||
def autofix_paused(state_json):
|
||||
"""True when that location's AUTO-FIX key says it was paused by hand ("AUTO-FIX\\nOFF\\nTIL ..."), None without one."""
|
||||
try:
|
||||
af = json.loads(state_json or "{}").get("autofix")
|
||||
except ValueError:
|
||||
return None
|
||||
if not af:
|
||||
return None
|
||||
lines = str(af.get("x") or "").split("\n")
|
||||
return len(lines) > 2 and lines[1] == "OFF" and lines[2].startswith("TIL")
|
||||
|
||||
|
||||
def all_autofix_summary(states, now):
|
||||
"""states: {id: that location's /api/state "state" JSON}. -> what NA's ALL AUTO-FIX key shows."""
|
||||
paused = sorted(cid.upper() for cid, s in states.items() if autofix_paused(s))
|
||||
return {"t": int(now), "paused": len(paused), "x": ("%s\nPAUSED" % " ".join(paused)) if paused else "ALL ON"}
|
||||
|
||||
|
||||
def ensure_variables(cfg, ids):
|
||||
from settings import campus
|
||||
have, _ = campus.companion_state(cfg)
|
||||
want = [n for cid in ids for n in ("sp_state_" + cid, "sp_hb_" + cid) if n not in have]
|
||||
want += [n for n in (VIEW["state"], VIEW["heartbeat"], SELECT, VIEW_CMD, MODE, REMOTE) if n not in have]
|
||||
want += [n for n in (VIEW["state"], VIEW["heartbeat"], SELECT, VIEW_CMD, MODE, REMOTE, ALL_CMD, ALL_AF) if n not in have]
|
||||
if not want:
|
||||
return
|
||||
comp = cfg.get("companion") or {}
|
||||
@@ -342,6 +382,31 @@ class Mirror:
|
||||
save(self.site.data_dir, data)
|
||||
return data
|
||||
|
||||
def all_autofix(self, cfg, mine, others):
|
||||
"""NA's ALL AUTO-FIX key: show which locations are paused; a tap pauses them all, or resumes them all."""
|
||||
states = {cid: (self.remote.get(cid) or {}).get("state") for cid in others}
|
||||
states[mine] = self.site.comp.get_custom((cfg.get("variables") or {}).get("state", "sp_state"))
|
||||
summary = all_autofix_summary(states, time.time())
|
||||
self.site.comp.set_custom(ALL_AF, json.dumps(summary, separators=(",", ":")))
|
||||
text = (self.site.comp.get_custom(ALL_CMD) or "").strip(" ;")
|
||||
if not text:
|
||||
return
|
||||
self.site.comp.set_custom(ALL_CMD, "")
|
||||
stamps = [w for w in text.replace(";", " ").split() if w.isdigit()]
|
||||
if not stamps or time.time() - int(stamps[-1]) > 30:
|
||||
return # pressed while this was down: not acted on later
|
||||
word = "auto" if summary["paused"] else "off"
|
||||
hosts = {cid: urllib.parse.urlparse(e["url"]).hostname for cid, e in others.items()}
|
||||
hosts[mine] = "127.0.0.1"
|
||||
for cid, host in sorted(hosts.items()):
|
||||
try:
|
||||
comp = self.site.comp if cid == mine else sp.Companion(host, 8000, timeout=3)
|
||||
before = (comp.get_custom("sp_cmd") or "").strip()
|
||||
comp.set_custom("sp_cmd", ";".join([before, "autofix " + word] if before else ["autofix " + word]))
|
||||
log.info("ALL AUTO-FIX: %s %s", cid, "paused" if word == "off" else "back to its hours")
|
||||
except OSError as e:
|
||||
log.warning("ALL AUTO-FIX: %s not reached: %s", cid, e)
|
||||
|
||||
def pass_on(self, pick, others, restart=False):
|
||||
"""A tap or hold on the LOCATION page goes to the shown location's watchdog, through its Companion."""
|
||||
text = (self.site.comp.get_custom(VIEW_CMD) or "").strip()
|
||||
@@ -388,6 +453,7 @@ class Mirror:
|
||||
t.join(2)
|
||||
if (data.get("pages") or {}).get("overview"):
|
||||
self.site.comp.set_custom(REMOTE, json.dumps(remote_summary(others, self.remote, time.time()), separators=(",", ":")))
|
||||
self.all_autofix(cfg, mine, others)
|
||||
# The LOCATION page shows the tapped location: its keys moved onto their cells.
|
||||
pick = (self.site.comp.get_custom(SELECT) or "").strip().lower() # held as "WS": the back key shows it
|
||||
if pick not in others and others:
|
||||
|
||||
Reference in New Issue
Block a user