1<html>
2
3<head>
4    <style>
5        * {
6            font-family: sans-serif;
7        }
8    </style>
9</head>
10<body>
11    Server Port <input id="port" type="text" value="8989"></input> <button id="connectButton" onclick="connect()">Connect</button><br>
12    <div id="socketState"></div>
13    <br>
14    <div id="buttons"></div><br>
15    <hr>
16    <button onclick="onGetPlayStatusButtonClicked()">Get Play Status</button><br>
17    <div id="getPlayStatusResponseTable"></div>
18    <hr>
19    <button onclick="onGetElementAttributesButtonClicked()">Get Element Attributes</button><br>
20    <div id="getElementAttributesResponseTable"></div>
21    <hr>
22    <table>
23        <tr>
24            <b>VOLUME</b>:
25            <button onclick="onVolumeDownButtonClicked()">-</button>
26            <button onclick="onVolumeUpButtonClicked()">+</button>&nbsp;
27            <span id="volumeText"></span><br>
28        </tr>
29        <tr>
30            <td><b>PLAYBACK STATUS</b></td><td><span id="playbackStatusText"></span></td>
31        </tr>
32        <tr>
33            <td><b>POSITION</b></td><td><span id="positionText"></span></td>
34        </tr>
35        <tr>
36            <td><b>TRACK</b></td><td><span id="trackText"></span></td>
37        </tr>
38        <tr>
39            <td><b>ADDRESSED PLAYER</b></td><td><span id="addressedPlayerText"></span></td>
40        </tr>
41        <tr>
42            <td><b>UID COUNTER</b></td><td><span id="uidCounterText"></span></td>
43        </tr>
44        <tr>
45            <td><b>SUPPORTED EVENTS</b></td><td><span id="supportedEventsText"></span></td>
46        </tr>
47        <tr>
48            <td><b>PLAYER SETTINGS</b></td><td><div id="playerSettingsTable"></div></td>
49        </tr>
50    </table>
51    <script>
52        const portInput = document.getElementById("port")
53        const connectButton = document.getElementById("connectButton")
54        const socketState = document.getElementById("socketState")
55        const volumeText = document.getElementById("volumeText")
56        const positionText = document.getElementById("positionText")
57        const trackText = document.getElementById("trackText")
58        const playbackStatusText = document.getElementById("playbackStatusText")
59        const addressedPlayerText = document.getElementById("addressedPlayerText")
60        const uidCounterText = document.getElementById("uidCounterText")
61        const supportedEventsText = document.getElementById("supportedEventsText")
62        const playerSettingsTable = document.getElementById("playerSettingsTable")
63        const getPlayStatusResponseTable = document.getElementById("getPlayStatusResponseTable")
64        const getElementAttributesResponseTable = document.getElementById("getElementAttributesResponseTable")
65        let socket
66        let volume = 0
67
68        const keyNames = [
69            "SELECT",
70            "UP",
71            "DOWN",
72            "LEFT",
73            "RIGHT",
74            "RIGHT_UP",
75            "RIGHT_DOWN",
76            "LEFT_UP",
77            "LEFT_DOWN",
78            "ROOT_MENU",
79            "SETUP_MENU",
80            "CONTENTS_MENU",
81            "FAVORITE_MENU",
82            "EXIT",
83            "NUMBER_0",
84            "NUMBER_1",
85            "NUMBER_2",
86            "NUMBER_3",
87            "NUMBER_4",
88            "NUMBER_5",
89            "NUMBER_6",
90            "NUMBER_7",
91            "NUMBER_8",
92            "NUMBER_9",
93            "DOT",
94            "ENTER",
95            "CLEAR",
96            "CHANNEL_UP",
97            "CHANNEL_DOWN",
98            "PREVIOUS_CHANNEL",
99            "SOUND_SELECT",
100            "INPUT_SELECT",
101            "DISPLAY_INFORMATION",
102            "HELP",
103            "PAGE_UP",
104            "PAGE_DOWN",
105            "POWER",
106            "VOLUME_UP",
107            "VOLUME_DOWN",
108            "MUTE",
109            "PLAY",
110            "STOP",
111            "PAUSE",
112            "RECORD",
113            "REWIND",
114            "FAST_FORWARD",
115            "EJECT",
116            "FORWARD",
117            "BACKWARD",
118            "ANGLE",
119            "SUBPICTURE",
120            "F1",
121            "F2",
122            "F3",
123            "F4",
124            "F5",
125        ]
126
127        document.addEventListener('keydown', onKeyDown)
128        document.addEventListener('keyup', onKeyUp)
129
130        const buttons = document.getElementById("buttons")
131        keyNames.forEach(name => {
132            const button = document.createElement("BUTTON")
133            button.appendChild(document.createTextNode(name))
134            button.addEventListener("mousedown", event => {
135                send({type: 'send-key-down', key: name})
136            })
137            button.addEventListener("mouseup", event => {
138                send({type: 'send-key-up', key: name})
139            })
140            buttons.appendChild(button)
141        })
142
143        updateVolume(0)
144
145        function connect() {
146            socket = new WebSocket(`ws://localhost:${portInput.value}`);
147            socket.onopen = _ => {
148                socketState.innerText = 'OPEN'
149                connectButton.disabled = true
150            }
151            socket.onclose = _ => {
152                socketState.innerText = 'CLOSED'
153                connectButton.disabled = false
154            }
155            socket.onerror = (error) => {
156                socketState.innerText = 'ERROR'
157                console.log(`ERROR: ${error}`)
158                connectButton.disabled = false
159            }
160            socket.onmessage = (message) => {
161                onMessage(JSON.parse(message.data))
162            }
163        }
164
165        function send(message) {
166            if (socket && socket.readyState == WebSocket.OPEN) {
167                socket.send(JSON.stringify(message))
168            }
169        }
170
171        function hmsText(position) {
172            const h_1 = 1000 * 60 * 60
173            const h = Math.floor(position / h_1)
174            position -= h * h_1
175            const m_1 = 1000 * 60
176            const m = Math.floor(position / m_1)
177            position -= m * m_1
178            const s_1 = 1000
179            const s = Math.floor(position / s_1)
180            position -= s * s_1
181
182            return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}:${position}`
183        }
184
185        function setTableHead(table, columns) {
186            let thead = table.createTHead()
187            let row = thead.insertRow()
188            for (let column of columns) {
189                let th = document.createElement("th")
190                let text = document.createTextNode(column)
191                th.appendChild(text)
192                row.appendChild(th)
193            }
194        }
195
196        function createTable(rows) {
197            const table = document.createElement("table")
198
199            if (rows.length != 0) {
200                columns = Object.keys(rows[0])
201                setTableHead(table, columns)
202            }
203            for (let element of rows) {
204                let row = table.insertRow()
205                for (key in element) {
206                    let cell = row.insertCell()
207                    let text = document.createTextNode(element[key])
208                    cell.appendChild(text)
209                }
210            }
211            return table
212        }
213
214        function onMessage(message) {
215            console.log(message)
216            if (message.type == "set-volume") {
217                updateVolume(message.params.volume)
218            } else if (message.type == "supported-events") {
219                supportedEventsText.innerText = JSON.stringify(message.params.events)
220            } else if (message.type == "playback-position-changed") {
221                positionText.innerText = hmsText(message.params.position)
222            } else if (message.type == "playback-status-changed") {
223                playbackStatusText.innerText = message.params.status
224            } else if (message.type == "player-settings-changed") {
225                playerSettingsTable.replaceChildren(message.params.settings)
226            } else if (message.type == "track-changed") {
227                trackText.innerText = message.params.identifier
228            } else if (message.type == "addressed-player-changed") {
229                addressedPlayerText.innerText = JSON.stringify(message.params.player)
230            } else if (message.type == "uids-changed") {
231                uidCounterText.innerText = message.params.uid_counter
232            } else if (message.type == "get-play-status-response") {
233                getPlayStatusResponseTable.replaceChildren(message.params)
234            } else if (message.type == "get-element-attributes-response") {
235                getElementAttributesResponseTable.replaceChildren(createTable(message.params))
236            }
237        }
238
239        function updateVolume(newVolume) {
240            volume = newVolume
241            volumeText.innerText = `${volume} (${Math.round(100*volume/0x7F)}%)`
242        }
243
244        function onKeyDown(event) {
245            console.log(event)
246            send({ type: 'send-key-down', key: event.key })
247        }
248
249        function onKeyUp(event) {
250            console.log(event)
251            send({ type: 'send-key-up', key: event.key })
252        }
253
254        function onVolumeUpButtonClicked() {
255            updateVolume(Math.min(volume + 5, 0x7F))
256            send({ type: 'set-volume', volume })
257        }
258
259        function onVolumeDownButtonClicked() {
260            updateVolume(Math.max(volume - 5, 0))
261            send({ type: 'set-volume', volume })
262        }
263
264        function onGetPlayStatusButtonClicked() {
265            send({ type: 'get-play-status', volume })
266        }
267
268        function onGetElementAttributesButtonClicked() {
269            send({ type: 'get-element-attributes' })
270        }
271</script>
272</body>
273
274</html>