Add local ADB web remote for Android TV
Node/Express server shells out to adb (connect/pair/keyevent/text/app launch) and serves a mobile-first installable PWA remote: power, mute, input switch, volume/channel rockers, D-pad, media transport, text entry, and one-tap streaming app launch. Supports multiple saved TVs and the Android 11+ wireless-debugging pairing flow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RSVyYcrjrbmrMKT85G4fyn
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no" />
|
||||
<title>TV Remote</title>
|
||||
<meta name="theme-color" content="#0b0d12" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="icon" href="/icons/icon-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<link rel="stylesheet" href="/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="topbar">
|
||||
<div class="device-picker">
|
||||
<select id="deviceSelect" aria-label="Select TV"></select>
|
||||
<span id="statusDot" class="status-dot" title="Connection status"></span>
|
||||
</div>
|
||||
<button id="settingsBtn" class="icon-btn" aria-label="Settings">⚙</button>
|
||||
</header>
|
||||
|
||||
<main id="remote" class="remote">
|
||||
<div class="row row-top">
|
||||
<button class="btn btn-round" data-key="POWER" aria-label="Power">⏻</button>
|
||||
<button class="btn btn-round" data-key="TV_INPUT" aria-label="Input">⇄</button>
|
||||
<button class="btn btn-round" data-action="mute" aria-label="Mute">🔇</button>
|
||||
</div>
|
||||
|
||||
<div class="dpad">
|
||||
<button class="btn dpad-up" data-key="DPAD_UP" aria-label="Up">▲</button>
|
||||
<button class="btn dpad-left" data-key="DPAD_LEFT" aria-label="Left">◀</button>
|
||||
<button class="btn dpad-center" data-key="DPAD_CENTER" aria-label="OK">OK</button>
|
||||
<button class="btn dpad-right" data-key="DPAD_RIGHT" aria-label="Right">▶</button>
|
||||
<button class="btn dpad-down" data-key="DPAD_DOWN" aria-label="Down">▼</button>
|
||||
</div>
|
||||
|
||||
<div class="row row-nav">
|
||||
<button class="btn" data-key="BACK" aria-label="Back">⤺ Back</button>
|
||||
<button class="btn" data-key="HOME" aria-label="Home">⌂ Home</button>
|
||||
<button class="btn" data-key="MENU" aria-label="Menu">☰ Menu</button>
|
||||
</div>
|
||||
|
||||
<div class="row row-vol-ch">
|
||||
<div class="rocker" data-rocker="volume">
|
||||
<span class="rocker-label">VOL</span>
|
||||
<button class="btn rocker-btn" data-key="VOLUME_UP" aria-label="Volume up">+</button>
|
||||
<button class="btn rocker-btn" data-key="VOLUME_DOWN" aria-label="Volume down">−</button>
|
||||
</div>
|
||||
<div class="rocker" data-rocker="channel">
|
||||
<span class="rocker-label">CH</span>
|
||||
<button class="btn rocker-btn" data-key="CHANNEL_UP" aria-label="Channel up">+</button>
|
||||
<button class="btn rocker-btn" data-key="CHANNEL_DOWN" aria-label="Channel down">−</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-media">
|
||||
<button class="btn" data-key="MEDIA_REWIND" aria-label="Rewind">⏪</button>
|
||||
<button class="btn" data-key="MEDIA_PLAY_PAUSE" aria-label="Play/Pause">⏯</button>
|
||||
<button class="btn" data-key="MEDIA_FAST_FORWARD" aria-label="Fast forward">⏩</button>
|
||||
</div>
|
||||
|
||||
<form class="row row-search" id="searchForm">
|
||||
<input id="searchInput" type="text" inputmode="text" placeholder="Type to search…" autocomplete="off" />
|
||||
<button type="submit" class="btn">Send</button>
|
||||
</form>
|
||||
|
||||
<div class="apps" id="appsRow"></div>
|
||||
</main>
|
||||
|
||||
<dialog id="settingsDialog">
|
||||
<h2>TVs</h2>
|
||||
<ul id="deviceList" class="device-list"></ul>
|
||||
|
||||
<form id="addDeviceForm" class="add-device-form">
|
||||
<input id="newDeviceName" type="text" placeholder="Name (e.g. Living Room)" autocomplete="off" />
|
||||
<input id="newDeviceHost" type="text" placeholder="IP address (e.g. 192.168.1.50)" autocomplete="off" required />
|
||||
<button type="submit" class="btn">Add TV</button>
|
||||
</form>
|
||||
|
||||
<details class="pair-details">
|
||||
<summary>Pair a new Android 11+ TV</summary>
|
||||
<form id="pairForm" class="pair-form">
|
||||
<p>On the TV: Settings → System → Developer options → Wireless debugging → Pair device with pairing code.</p>
|
||||
<input id="pairHost" type="text" placeholder="Pairing IP:port (e.g. 192.168.1.50:41231)" autocomplete="off" required />
|
||||
<input id="pairCode" type="text" inputmode="numeric" placeholder="6-digit code" autocomplete="off" required />
|
||||
<button type="submit" class="btn">Pair</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<p id="settingsMessage" class="settings-message" role="status"></p>
|
||||
|
||||
<button id="closeSettings" class="btn btn-close">Close</button>
|
||||
</dialog>
|
||||
|
||||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||||
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user