{"id":51282525,"url":"https://github.com/vb3/atvr4samsung","last_synced_at":"2026-06-30T03:00:51.640Z","repository":{"id":368294178,"uuid":"1284445167","full_name":"vb3/atvr4samsung","owner":"vb3","description":"Apple TV Remote for Samsung: emulate an Apple TV so the iPhone's native Control Center remote controls a Samsung Frame TV (Companion Link → Samsung WebSocket + Wake-on-LAN).","archived":false,"fork":false,"pushed_at":"2026-06-29T23:51:46.000Z","size":123,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-30T00:17:19.952Z","etag":null,"topics":["apple-tv","companion","frame-tv","homekit","ios","mdns","raspberry-pi","remote-control","samsung","tizen"],"latest_commit_sha":null,"homepage":"https://github.com/vb3/atvr4samsung#readme","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vb3.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-29T21:46:45.000Z","updated_at":"2026-06-29T23:51:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vb3/atvr4samsung","commit_stats":null,"previous_names":["vb3/atvr4samsung"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/vb3/atvr4samsung","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vb3%2Fatvr4samsung","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vb3%2Fatvr4samsung/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vb3%2Fatvr4samsung/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vb3%2Fatvr4samsung/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vb3","download_url":"https://codeload.github.com/vb3/atvr4samsung/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vb3%2Fatvr4samsung/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34950333,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-tv","companion","frame-tv","homekit","ios","mdns","raspberry-pi","remote-control","samsung","tizen"],"created_at":"2026-06-30T03:00:51.008Z","updated_at":"2026-06-30T03:00:51.630Z","avatar_url":"https://github.com/vb3.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# atvr4samsung\n\n[![CI](https://github.com/vb3/atvr4samsung/actions/workflows/tests.yml/badge.svg)](https://github.com/vb3/atvr4samsung/actions/workflows/tests.yml)\n[![Release](https://github.com/vb3/atvr4samsung/actions/workflows/release.yml/badge.svg)](https://github.com/vb3/atvr4samsung/actions/workflows/release.yml)\n[![Latest release](https://img.shields.io/github/v/release/vb3/atvr4samsung)](https://github.com/vb3/atvr4samsung/releases/latest)\n\nEmulate an Apple TV so the iPhone's **native** Control Center remote pairs with it, then relay each\ncommand to a **Samsung Frame TV** over its local WebSocket API. Control the Frame with the stock iOS\nremote — no custom app, no jailbreak.\n\n\u003e **What's in a name?** **atvr** = **A**pple **TV** **R**emote, so `atvr4samsung` is \"Apple TV Remote\n\u003e for Samsung\" — drive a Samsung TV with the iPhone's built-in Apple TV Remote.\n\n\u003e **Status: working.** A real iPhone (iOS 26) pairs with the emulated Apple TV, the remote stays\n\u003e connected, and D-pad/Select/Menu/Home/Play-Pause + swipes + **Volume/Mute** + **Power** + **keyboard\n\u003e text entry** (into the TV's system search/browser fields) drive the Frame. The Apple-side server is a\n\u003e first-party Companion Link implementation (originally derived from\n\u003e [pyatv](https://github.com/postlund/pyatv), MIT), with pair-once auth hardening. See\n\u003e [`docs/hld.md`](docs/hld.md) and [`docs/lld.md`](docs/lld.md) for the design and the iOS-26\n\u003e capability gates, and [`docs/operations.md`](docs/operations.md) to install/run/troubleshoot.\n\n## How it works\n\n```\n iPhone (iOS 26)            Raspberry Pi 4 (IoT VLAN, same subnet as TV)        Samsung Frame TV\n native Apple TV ─Companion─▶ ┌──────────────────────────────────────┐ ──WS──▶ 192.168.1.50\n Remote (Control   Link/mDNS  │ Companion SERVER (emulated Apple TV)  │ :8002   (token auth)\n Center)                      │   └─▶ command mapper (_hidC/_hidT →   │ +UDP/9  Wake-on-LAN\n                              │        Samsung KEY_*) └─▶ Samsung client\n                              └──────────────────────────────────────┘\n```\n\n- **Apple side** — advertises `_companion-link._tcp` and speaks Companion Link (pairing + encrypted\n  session + HID command frames). First-party implementation (OPACK/SRP/AEAD), with pair-once auth\n  hardening.\n- **Samsung side** — sends Tizen `KEY_*` commands over the TV's WebSocket remote API and a\n  Wake-on-LAN packet for power-on.\n\nThe target deployment is a Raspberry Pi 4 on the **same IoT VLAN as the TV**, with an existing mDNS\nreflector bridging discovery to the phone's VLAN.\n\n## Repository layout\n\n```\nsrc/atvr4samsung/\n  config.py             typed config loader (dataclasses; yaml imported lazily)\n  bridge/keymap.py      Apple button -\u003e Samsung KEY_* map + play/pause toggle   (pure, tested)\n  bridge/gestures.py    swipe/tap -\u003e discrete direction state machine           (pure, tested)\n  samsung/client.py     async Samsung Frame control client + Wake-on-LAN\n  companion/discovery.py  mDNS advertisement of the Companion service\n  companion/server.py   emulated Apple TV bridge (relays decoded commands to Samsung)\n  companion/protocol/   first-party Companion Link impl (opack, chacha20, tlv8, auth, appletv)\n  app.py                console entry point (`atvr4samsung`)\nscripts/                installer (`install.sh`)\ntests/                  stdlib-runnable unit tests for the pure layers\ndocs/hld.md             high-level design (architecture, decisions)\ndocs/lld.md             low-level design (modules, wire protocol, iOS-26 gates, mappings)\ndocs/operations.md      install / run / upgrade / troubleshoot\nAGENTS.md               coding conventions (incl. testing philosophy)\n```\n\n## Install (Linux / Raspberry Pi)\n\nInstalls as an isolated **pipx** app and runs as a **systemd** service. The recommended path installs\nthe latest published GitHub Release wheel; full details and troubleshooting are in\n[`docs/operations.md`](docs/operations.md). The CLI defaults to\n`~/.config/atvr4samsung/config.yaml`, so the commands below do not need `--config` unless you\nchoose a non-standard path.\n\n**Latest GitHub Release wheel**:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/vb3/atvr4samsung/main/scripts/install.sh | bash\nnano ~/.config/atvr4samsung/config.yaml   # set TV host/MAC + a strong PIN\natvr4samsung --check                      # validate (no network)\natvr4samsung install-service --apply      # install + start the systemd service (uses sudo)\n```\n\nThe installer resolves the wheel from\n[`releases/latest`](https://github.com/vb3/atvr4samsung/releases/latest), writes the default\nconfig, and does not start the service unless `SERVICE=1` is set.\n\n**From a clone / latest main**:\n\n```bash\ngit clone https://github.com/vb3/atvr4samsung \u0026\u0026 cd atvr4samsung\nSOURCE=. bash scripts/install.sh\nnano ~/.config/atvr4samsung/config.yaml   # set TV host/MAC + a strong PIN\natvr4samsung --check                      # validate (no network)\natvr4samsung install-service --apply      # install + start the systemd service (uses sudo)\n```\n\nWe don't ship a `.deb` — pipx already gives an isolated, reproducible install. See\n`docs/operations.md` §1.\n\nFrom a clone (dev): `python -m venv .venv \u0026\u0026 . .venv/bin/activate \u0026\u0026 pip install -e .`.\n\n**Use it:** on the iPhone, Control Center → Apple TV Remote → pick **your configured TV name** →\nenter your PIN. D-pad/Select/Menu/Home/Play-Pause + swipes drive the TV. Manage:\n`systemctl status|restart|stop atvr4samsung`, logs `journalctl -u atvr4samsung -f`.\n\n`config.yaml`, the PIN, and the Samsung token file are **gitignored** — never commit them.\n\n## Pairing the Samsung TV (one-time \"Allow\" prompt)\n\nPairing has **two independent sides**: the iPhone pairs with the bridge (PIN, above), and the **bridge\npairs with the TV** (a one-time on-screen approval). The first time the bridge sends a command, the TV\npops an **Allow / Deny** prompt naming the remote — by default **`atvr4samsung`** (this is the\n`samsung.name` value in your config). You must choose **Allow** on the TV with its physical remote.\n\nWhat happens, step by step:\n\n1. After install + pairing, press any button on the iPhone (e.g. **Volume**). Make sure the **TV is\n   on** — the bridge sends a Wake-on-LAN packet first, but the Allow prompt only shows once the TV is\n   awake.\n2. The TV displays *\"Allow `atvr4samsung` to connect?\"* (wording varies by model). Select **Allow**.\n3. The TV returns an access token, which the bridge saves to `samsung.token_file` (default\n   `~/.local/state/atvr4samsung/samsung-token.txt`). **All later connects are silent** — you won't be\n   asked again.\n\nNotes:\n\n- This works only on TV port **8002** (TLS + persistent token), which is the default. Port **8001**\n  re-prompts on *every* connect — don't use it for the always-on service.\n- If you miss the prompt, tap **Deny**, or delete the token file, the TV simply prompts again on the\n  next command — accept it and you're set.\n- The first command (or the first after the TV sleeps) can take a few seconds while the TV wakes and\n  the WebSocket connects; that's expected.\n- To revoke access, remove the granted device on the TV (Samsung **Settings → General → External\n  Device Manager → Device Connection Manager → Device List**, names vary by year) and delete\n  `samsung-token.txt`.\n\nRun `atvr4samsung doctor` to check the TV is reachable and the token path is writable before you start.\n\n## Update\n\nRe-run the installer to reinstall the latest published Release wheel, then restart the service:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/vb3/atvr4samsung/main/scripts/install.sh | bash\nsudo systemctl restart atvr4samsung\n```\n\nThe installer writes the config only if it's missing, so your `config.yaml`, pairing, and Samsung\ntoken are preserved across updates. Prefer not to pipe a script? Grab the wheel URL from\n[`releases/latest`](https://github.com/vb3/atvr4samsung/releases/latest) and run it yourself:\n\n```bash\npipx install --force \"\u003clatest release wheel URL\u003e\"   # or a clone: SOURCE=. bash scripts/install.sh\nsudo systemctl restart atvr4samsung\n```\n\nPublished wheels are the `X.Y.0` stable cuts (patch bumps aren't published); use\n`SOURCE=git+https://github.com/vb3/atvr4samsung` for the latest `main`. Details in\n[`docs/operations.md`](docs/operations.md) §5.\n\n## Uninstall\n\n```bash\nsudo systemctl disable --now atvr4samsung        # stop + unregister the service\nsudo rm -f /etc/systemd/system/atvr4samsung.service \u0026\u0026 sudo systemctl daemon-reload\npipx uninstall atvr4samsung                       # remove the app (or: pip uninstall)\nrm -rf ~/.config/atvr4samsung                     # config (forget the device on the iPhone too)\n```\n\n## Testing\n\nPure-logic unit tests run with stdlib only (no TV, no phone, no Apple-protocol deps):\n```bash\npython -m pytest          # or: python -m unittest discover -s tests\n```\nSee `AGENTS.md` for the testing philosophy (meaningful over superficial).\n\n## License \u0026 attribution\n\nMIT — see [`LICENSE`](LICENSE). The Companion server is derived from pyatv (MIT); also uses\n`samsungtvws` (LGPL-3.0, import-only), `zeroconf` (LGPL-2.1), `cryptography`, `srptools`, and\n`wakeonlan`. Full notices in [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).\n\nThis project emulates an Apple TV for personal interoperability with hardware you own. \"Apple TV\" and\n\"Samsung Frame\" are trademarks of their respective owners; this project is not affiliated with or\nendorsed by either.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvb3%2Fatvr4samsung","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvb3%2Fatvr4samsung","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvb3%2Fatvr4samsung/lists"}