feat(x4): add per-camera manual wake and hide power action when connected
This commit is contained in:
@@ -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 (
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user