|
| 1 | +# @agent-device/tizen |
| 2 | + |
| 3 | +Drive web apps on Samsung Tizen TVs with [agent-device](https://agent-device.dev). The plugin |
| 4 | +debug-launches the app over `sdb`, forwards its DevTools port, and drives the page over the Chrome |
| 5 | +DevTools Protocol: remote keys, pointer, text, snapshots, screenshots and console logs. |
| 6 | + |
| 7 | +Status: experimental. Verified on a Samsung Smart Monitor M7 (Tizen 9.0, packaged build of a React |
| 8 | +Native Web app): `connect`, `open`, `open --relaunch`, `snapshot -i`, `tv-remote` (directions, |
| 9 | +select, back), `back`, `is focused`, `press`, `find ... click`, `hover`, `longpress`, `fill`, |
| 10 | +`type`, `wait`, `scroll`, `screenshot`, `logs`, `apps`, `appstate`, `close`, `disconnect`. |
| 11 | + |
| 12 | +## How it plugs in |
| 13 | + |
| 14 | +agent-device leases only iOS, Android and HarmonyOS devices, so a Tizen TV enters as an |
| 15 | +**Android TV device that this plugin owns** (`platform: android`, `target: tv`, id |
| 16 | +`tizen:<sdb serial>`). Every command on that device goes to this plugin's runtime, never to adb. |
| 17 | + |
| 18 | +## Requirements |
| 19 | + |
| 20 | +- `sdb` from the Tizen SDK of the VS Code Tizen extension, set up with its Set Up step. The plugin |
| 21 | + finds it in the SDK that `$TIZEN_SDK` or `~/.tizen.path.config` names, or at the `sdbPath` |
| 22 | + option; it is not on `PATH` |
| 23 | +- The TV in Developer Mode with this host's IP set as the developer IP; see Samsung's |
| 24 | + [Connecting the TV and SDK](https://developer.samsung.com/smarttv/develop/getting-started/using-sdk/tv-device.html) |
| 25 | +- The app installed on the TV from a debug-enabled package |
| 26 | +- The TV and this host on the same network |
| 27 | + |
| 28 | +## Install |
| 29 | + |
| 30 | +```sh |
| 31 | +agent-device plugins add @agent-device/tizen |
| 32 | +agent-device daemon stop # the next command starts a daemon with the plugin loaded |
| 33 | +``` |
| 34 | + |
| 35 | +Plugin options: `sdbPath` (the `sdb` binary, when the SDK is not found) and `device` (default TV |
| 36 | +IP or sdb serial). |
| 37 | + |
| 38 | +To try a local build, pack the package and install the tarball into a plugin store installation: |
| 39 | + |
| 40 | +```sh |
| 41 | +pnpm --filter @agent-device/tizen pack --out /tmp/agent-device-tizen.tgz |
| 42 | +installation=$(uuidgen) |
| 43 | +npm install --prefix ~/.agent-device/plugins/$installation /tmp/agent-device-tizen.tgz |
| 44 | +``` |
| 45 | + |
| 46 | +Then register `{"plugins": {"@agent-device/tizen": {"installation": "<installation>"}}}` in |
| 47 | +`~/.agent-device/config.json` and run `agent-device daemon stop`. |
| 48 | + |
| 49 | +## Use |
| 50 | + |
| 51 | +```sh |
| 52 | +agent-device daemon stop # the lease TTL is read when the daemon starts |
| 53 | +export AGENT_DEVICE_LEASE_TTL_MS=1800000 AGENT_DEVICE_LEASE_MAX_TTL_MS=1800000 # see Known limitations |
| 54 | +export AGENT_DEVICE_SESSION=checkout-tv # one session name per agent |
| 55 | +agent-device connect tizen --device 192.0.2.10 # TV IP or sdb serial |
| 56 | +agent-device open <appId> # kill, debug launch, forward, attach over CDP |
| 57 | +agent-device wait text <text on the first screen> |
| 58 | +agent-device tv-remote press down |
| 59 | +agent-device snapshot -i |
| 60 | +agent-device screenshot ./tv.png |
| 61 | +agent-device logs start # then logs path |
| 62 | +agent-device close <appId> |
| 63 | +agent-device disconnect |
| 64 | +``` |
| 65 | + |
| 66 | +`--device` takes the TV IP (`sdb connect <ip>:26101` runs when it is not connected yet) or an sdb |
| 67 | +serial. Without it, the plugin uses the `device` option, then the only TV connected to `sdb`. |
| 68 | +`connect` does not contact the TV; `open` connects it. |
| 69 | + |
| 70 | +The TV is a leased device, so every command from `connect` to `disconnect` names its session with |
| 71 | +`AGENT_DEVICE_SESSION`, `--session <name>` or a config session. Without one, the command fails with |
| 72 | +`REMOTE_SESSION_REQUIRED`. |
| 73 | + |
| 74 | +## Supported |
| 75 | + |
| 76 | +| Command | How | |
| 77 | +| --- | --- | |
| 78 | +| `open` / `close` | `sdb shell 0 was_kill`, `sdb shell 0 debug` (prints the DevTools port), `sdb forward`, then CDP / `sdb shell 0 was_kill` | |
| 79 | +| `tv-remote press up/down/left/right/select/back`, `back` | `Input.dispatchKeyEvent` with the Samsung remote codes (Back = `XF86Back`, 10009) | |
| 80 | +| `appstate` | the attached app while its page is visible | |
| 81 | +| `apps` | `sdb shell 0 vd_applist`: user-installed apps with their titles | |
| 82 | +| `logs` | console calls, exceptions and browser log entries of the attached page, one line each | |
| 83 | +| `snapshot`, `find`, `is`, `get`, `wait` | DOM walk over `Runtime.evaluate`: visible interactive, labelled or text nodes, focus from `document.activeElement`; `-s`, `-d` and `-i` apply to the walk | |
| 84 | +| `screenshot` | `Page.captureScreenshot` | |
| 85 | +| `click`, `press <x> <y>`, `longpress`, `hover` | `Input.dispatchMouseEvent` | |
| 86 | +| `fill` | pointer press on the field, select its contents, then `Input.insertText`, or Backspace for an empty value | |
| 87 | +| `type` | `Input.insertText` into the focused element | |
| 88 | +| `scroll` | `mouseWheel` at the centre of the scroll container or viewport, by the requested distance | |
| 89 | + |
| 90 | +Not yet: system buttons (`home`, menu, volume, channel and media keys), `network dump`, `perf`, |
| 91 | +`install` and `uninstall`, recording, URLs and deep links in `open`, `power`. |
| 92 | + |
| 93 | +## Known limitations |
| 94 | + |
| 95 | +- **`open` is always a cold start.** `sdb shell 0 debug` launches the app and cannot attach to a |
| 96 | + running one, so `open` kills the app first. `open` returns once the document has loaded, which |
| 97 | + can be before the app leaves its splash screen; wait for app content (`wait text ...`) before |
| 98 | + the first snapshot. |
| 99 | +- **Idle leases expire after 60 s by default, and expiry ends the session.** agent-device gives a |
| 100 | + direct-device provider lease its default TTL and beats it only while a command runs. When the |
| 101 | + lease ends, by `disconnect` or by expiry, the plugin closes the page connection and removes the |
| 102 | + `sdb` forward. After a longer pause, the next command fails with `Lease is not active` or |
| 103 | + `No active session` and needs a new `connect` and `open`. To avoid it, run |
| 104 | + `agent-device daemon stop`, then start the daemon with `AGENT_DEVICE_LEASE_TTL_MS` and |
| 105 | + `AGENT_DEVICE_LEASE_MAX_TTL_MS` set (for example 30 min). |
| 106 | +- **`screenshot` captures the app page, not the TV screen.** The restricted `sdb shell 0` has no |
| 107 | + screen-capture command. When the app is in the background, the page is hidden and renders no |
| 108 | + frames, so `screenshot` fails at once; `open <appId>` brings the app back. While the on-screen |
| 109 | + keyboard is open, the page viewport and so the screenshot are shorter. |
| 110 | +- **`appstate` knows only the attached app.** The TV's REST API (`:8001/api/v2/applications/<id>`) |
| 111 | + reports `visible: false` for an app in front of the user, and `sdb shell 0` has no |
| 112 | + foreground-app command. |
| 113 | +- **Console messages from before the attach are replayed without object previews**, so objects |
| 114 | + in them show as `Object`. Messages logged after the attach show their properties. |
| 115 | +- **A second DevTools client on the same page can hang the session.** Close Chrome DevTools and |
| 116 | + other inspector clients attached to the app before a session. |
| 117 | +- **Snapshots are DOM-derived.** Labels come from `aria-label`, `alt`, `title` or own text; |
| 118 | + rects are CSS px, which match screenshot pixels only at `devicePixelRatio` 1 (1080p apps). |
| 119 | + The walk does not enter iframes or shadow roots. `is text` on an input compares its label, not |
| 120 | + its value; the value is in `snapshot --raw`. |
| 121 | +- **System overlays are outside the page.** The Samsung menu, on-screen keyboard and other system |
| 122 | + layers never appear in `screenshot` or `snapshot`. |
0 commit comments