feat(x4): add per-camera manual wake and hide power action when connected

This commit is contained in:
DCCONSTRUCTIONS
2026-09-10 16:27:48 +03:00
parent db14ee5435
commit 99f29218d7
27 changed files with 992 additions and 126 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ sys.path.insert(0, str(REPOSITORY / "scripts/packaging"))
from debian import package # noqa: E402
from fetch_sdk import verify # noqa: E402
VERSION = "0.1.3-7"
VERSION = "0.1.3-9"
WHEELS = {
"aiohappyeyeballs",
"aiohttp",
@@ -34,6 +34,7 @@ def build():
paths += list((REPOSITORY / "packages/plugin-sdk/python/missioncore_plugin_sdk").rglob("*.py"))
paths += [REPOSITORY / "scripts/packaging/debian.py"]
paths += [REPOSITORY / "apps/node-agent/packaging/insta360_profile.py"]
paths += [REPOSITORY / "apps/node-agent/packaging/install_owner_release.py"]
paths += [
REPOSITORY / name
for name in (
+31 -7
View File
@@ -47,6 +47,28 @@ class WakeService:
def save(self, ident, value):
write(self.root / (ident + ".json"), json.dumps(value).encode(), 0o600)
def registry(self):
"""Remember exact USB identities without enabling any recovery policy."""
with self.lock:
observed = connected()
known = {
p.stem for p in self.root.glob("instax4_*.json") if IDENTIFIER.fullmatch(p.stem)
}
for binding in observed:
ident = binding.device_id
if (
sum(x.device_id == ident for x in observed) != 1
or not re.fullmatch(r"[A-Za-z0-9]{7,64}", binding.serial)
or (ident not in known and len(known) >= 500)
):
continue
value = self.load(ident)
if "serial" not in value:
value["serial"] = binding.serial
self.save(ident, value)
known.add(ident)
return {"items": sorted(ident for ident in known if self.load(ident).get("serial"))}
def control(self, ident, enabled):
with self.lock:
value = self.load(ident)
@@ -65,20 +87,20 @@ class WakeService:
self.running[ident].terminate()
return {"enabled": enabled}
def wake(self, ident, attempt):
def wake(self, ident, attempt, manual=False):
if not self.advertiser.acquire(blocking=False):
return {"state": "busy"}
try:
# Installation cannot replace code/state while a wake is in flight.
with acquisition():
return self.wake_locked(ident, attempt)
return self.wake_locked(ident, attempt, manual)
finally:
self.advertiser.release()
def wake_locked(self, ident, attempt):
def wake_locked(self, ident, attempt, manual=False):
with self.lock:
value = self.load(ident)
if not value["enabled"] or "serial" not in value:
if (not manual and not value["enabled"]) or "serial" not in value:
return {"state": "disabled"}
serial = value["serial"]
if any(x.device_id == ident for x in connected()):
@@ -135,13 +157,15 @@ class WakeService:
state = "connected"
except (ValueError, OSError):
pass
return {"state": state if self.load(ident)["enabled"] else "disabled"}
return {"state": state if manual or self.load(ident)["enabled"] else "disabled"}
def dispatch(self, method, route, value, _headers):
if method == "GET" and route == "/health":
return {"ready": True}
if method != "POST" or not isinstance(value, dict):
raise ValueError("Unsupported recovery request")
if route == "/registry" and not value:
return self.registry()
ident = value.get("device_id")
if not isinstance(ident, str) or not IDENTIFIER.fullmatch(ident):
raise ValueError("Invalid recovery identity")
@@ -149,12 +173,12 @@ class WakeService:
if type(value["enabled"]) is not bool:
raise ValueError("Recovery requires an explicit boolean")
return self.control(ident, value["enabled"])
if route == "/wake" and set(value) == {"device_id", "attempt"}:
if route in ("/wake", "/wake-once") and set(value) == {"device_id", "attempt"}:
if not isinstance(value["attempt"], str) or not re.fullmatch(
r"[0-9a-f]{32}", value["attempt"]
):
raise ValueError("Invalid wake attempt")
return self.wake(ident, value["attempt"])
return self.wake(ident, value["attempt"], manual=route == "/wake-once")
raise ValueError("Unsupported recovery request")