The layout separates what is shared from what is LG's:
src/tv_privacy/
ssdp.py wol.py discovery and wake, brand-agnostic
config.py keystore.py host/brand resolution, client keys, XDG paths
runlog.py errors.py JSONL audit log, error taxonomy
brands/
__init__.py registry + fingerprint-based detection
lg/ SSAP transport, manifests, eulaStatus, lab tooling
cli/ commands, dispatching to a backend
A backend is a module exposing matches_fingerprint(found), open_session(...),
DEFAULT_SCOPE, and privacy / power submodules. Register it in
brands.BRANDS and tv-privacy discover will identify it automatically.
The interface is deliberately thin, and should stay that way until there are two. Designing an abstract backend against a single implementation is guessing. What is already known to differ:
- Transport. LG uses SSAP over a WebSocket with an on-screen pairing prompt. Samsung Tizen uses a different WebSocket protocol with token auth; Sony uses HTTP with a pre-shared key or PIN; Vizio uses HTTPS on 7345 with a PIN.
- Where consent lives. On LG it is one object-valued setting reachable through a generic settings API. No other vendor need expose it that way, or at all — the first thing to establish for a new brand is whether the consents are reachable over the network in any form.
- Discovery. SSDP is common but not universal, and fingerprints differ.
lab is LG-only and refuses to run against another brand rather than emitting a
wall of 404s that look like findings.
tests/fake_tv.py is a small webOS TV that replays recorded SSAP responses
distilled from the real probe logs, so pytest runs in CI with no hardware:
uv run pytestIt can also be told to misbehave in the two ways that matter — acknowledging a write without persisting it, and closing the socket mid-registration without an error frame — because both are how real firmware fails and neither can be provoked on demand.
The corpus in tests/fixtures/ is scrubbed of MAC addresses, LAN IPs and UPnP
UUIDs by tools/distill_logs.py, which refuses to write if anything
identifier-shaped survives.