From b388a01859db37f4986cef5432ab3c716678f049 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 16:20:44 +0200 Subject: [PATCH 01/26] Add webOS TV app metadata --- clients/webos/appinfo.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 clients/webos/appinfo.json diff --git a/clients/webos/appinfo.json b/clients/webos/appinfo.json new file mode 100644 index 0000000..79527dc --- /dev/null +++ b/clients/webos/appinfo.json @@ -0,0 +1,16 @@ +{ + "id": "com.lucaxtech.app.uplinkwitness", + "version": "0.1.0", + "vendor": "Luca Serioli", + "type": "web", + "main": "index.html", + "title": "UplinkWitness TV", + "appDescription": "TV client for the UplinkWitness Internet connection black box", + "icon": "icon.png", + "largeIcon": "largeicon.png", + "iconColor": "#111827", + "resolution": "1920x1080", + "splashBackground": "splash.png", + "splashColor": "#070b14", + "transparent": false +} From 5bc9c17a8a74836c79a9cce67e3f52ccd133ceb8 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:27:47 +0200 Subject: [PATCH 02/26] Refine webOS TV app metadata --- clients/webos/appinfo.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clients/webos/appinfo.json b/clients/webos/appinfo.json index 79527dc..2cba3bb 100644 --- a/clients/webos/appinfo.json +++ b/clients/webos/appinfo.json @@ -5,12 +5,10 @@ "type": "web", "main": "index.html", "title": "UplinkWitness TV", - "appDescription": "TV client for the UplinkWitness Internet connection black box", + "appDescription": "TV client for the UplinkWitness connection black box", "icon": "icon.png", "largeIcon": "largeicon.png", "iconColor": "#111827", "resolution": "1920x1080", - "splashBackground": "splash.png", - "splashColor": "#070b14", "transparent": false } From c6d52d3c56a66f0285ca013a5ccd2c458b9eabc6 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:28:23 +0200 Subject: [PATCH 03/26] Add installable webOS TV launcher shell --- clients/webos/index.html | 115 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 clients/webos/index.html diff --git a/clients/webos/index.html b/clients/webos/index.html new file mode 100644 index 0000000..a288b60 --- /dev/null +++ b/clients/webos/index.html @@ -0,0 +1,115 @@ + + + + + + UplinkWitness TV + + + + +
Yellow: server settings
+ +
+
+
UplinkWitness TV
+
Connect this TV to your UplinkWitness host.
+ + +
+
+ + +
+
Example: http://linewatch.local:8080 or http://192.168.x.x:8080
Use the Yellow remote key at any time to reopen this screen.
+
+
+ +
+
+
UplinkWitness
+
+
Opening dashboard…
+
+
+ + + + From 8e1d0507e453f87cc43093a903a0e1c5b4a4b861 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:29:45 +0200 Subject: [PATCH 04/26] Add webOS launcher icons --- clients/webos/icon.png | Bin 0 -> 498 bytes clients/webos/largeicon.png | Bin 0 -> 910 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 clients/webos/icon.png create mode 100644 clients/webos/largeicon.png diff --git a/clients/webos/icon.png b/clients/webos/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..50fd8cfd502489ad7bd0e9e3df79ccca3684f596 GIT binary patch literal 498 zcmVXfiuu^14=q1YK}sUL`TUD=s5#-;1U$pM$yhr>^PIealTj0j4~^~ zaef?WbGW*`S!Uv*cL1${uy-Kr9SD2pQvK%3$KQ@|D0qLqRlIe`Ej+q=uE{db6OL^^ zU+*%4s=(9RZ@88_ONE1Kch5D!T%s+Sv@F{$QB;!Q#tR#_CFIW1Wy!KFA&ig_rVnP{ zmY_J(NlpomWlONm(^Xm!J~Co|CepoHUrRUU{`-+GejdI`erEvTpc>z*wK0Z`agtv4 zIg54S-RU`oSV*xo@>1?46YGh`l=9pBFa6V>z?7i@ZzQcNRv-3x^M8*;#&8OO#>b&RHkZaQ2X6uG6J4 o-OU@FA08c4THb-McOdMYf99m7Mg{a-4*&oF07*qoM6N<$f_?_z^#A|> literal 0 HcmV?d00001 diff --git a/clients/webos/largeicon.png b/clients/webos/largeicon.png new file mode 100644 index 0000000000000000000000000000000000000000..5db4279bdbfe8334653b11a2934e9afd10b5189c GIT binary patch literal 910 zcmeAS@N?(olHy`uVBq!ia0vp^O(4v{1SDA#+gC9#Fo$}&IEGZrc{|sz@2Y`7+g^^& zCb4%1Bwu`In^NL&MW$7%v#%)MVSoAqzL+K@jZKfHud$tZ@^@~1{Py>`lg`%6EPQiP zi$DL~KJSA3xVyjk8>U=PV3JaCXlBWnz`!fCz>&$sqk)a{f&wF0aQ$Nb4_}|(f8iW+ zxL^PM+|Z~DIIoqewi@i z#q=q5=VljN*idM&pzKwZ=6l|yv`VAUUsltwOljoSdN0| zw+*h#9GO>M@TxtcbJIafCb@(Et~t={j&CpBv$_1Nf4}`xdpmo(o8LC;m+4`{}*%?3w2$p&$C+b=bKH2$4b3Z3)Mg#meTvYqXiZcJVyB^q7T~o8SK6u@a@rb z|5vFt_I9CtdkZJ;*w=mO;Aa7K`Kdc*>@%$hES}lEF1=*U!=tbFYAW_Emf5@P%HJCg zP6%bM7rmx-VZ+l|JMv%b(CB5s7Rrr}bas7z*2t*K`+PS0T&0jHZJBZs0+$SowWMlh zv)^5~D6t3@6`1kE5&vS|hROtmfWuqPGBXFdWd9$M>tIvonNlu;|8jghje7G8a_&Gm*hLVBJj@E}*$Emu!`~ zX1@4CfGe+W{uIxYW{!*r2NpH%D(O+QQCbmg*$_HO}S zrzta%FRf9W-qQ(+&D6t_c&98&=J{wSrmA*TI%@e@X_;TVlmENVKHZujzsUU7bD!?B zk+V;izWu$3KO$aruYqS&1!qLTg$Z23nHiF)_vY_U|9Eji-pmMzW5?sJmhQ|ixx8V6 zda{R1{++p|Iz0M^YoCcMQcRanT{!t#0dD{^I9))}G-gozU@xrT*e&O*>kZ6j44$rj JF6*2UngHdXgrfie literal 0 HcmV?d00001 From 1ef02e0bade833775820740809c69fe11af7339a Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:32:00 +0200 Subject: [PATCH 05/26] Turn wallboard into a webOS-friendly TV interface --- templates/wallboard.html | 197 ++++++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 43 deletions(-) diff --git a/templates/wallboard.html b/templates/wallboard.html index 064ec5d..8e1d4fe 100644 --- a/templates/wallboard.html +++ b/templates/wallboard.html @@ -2,55 +2,166 @@ - -UplinkWitness Wallboard - + +UplinkWitness TV + -
-
UplinkWitness Wallboard
Internet connection black box
-
-
-
Caricamento…
-
-
-
-
Ping Internet
-
-
-
WAN / PPPoE
-
-
-
WAN fisica
-
-
-
Uptime router
-
-
-
Temperatura router
-
-
-
Disponibilità 30 giorni
-
-
-
-
-
Latenza Internet · ultime 24 ore
-
Ultimo problema
Nessuno registrato
-
-
-
+
+
+
UplinkWitness TV
Internet connection black box
+
-
-
+
+ + + +
+
CARICAMENTO…
+
-
-
+
+ +
+
+
Internet ping
-
-
+
WAN / PPPoE
-
-
+
Disponibilità 30 giorni
-
-
+
WAN fisica
-
-
+
Router
-
-
+
Temperatura router
-
-
+
+
+
Latenza Internet · ultime 24 ore
+
Ultimo problema
Nessuno registrato
-
+
+
+ +
+
+
ICMP Internet
-
-
+
TCP connect
-
-
+
IPv6
-
-
+
DNS
-
Probe applicativo
+
HTTP
-
Probe connettività
+
Host link
-
+
Gateway neighbour
-
Linux neighbour / ARP evidence
+
Qualità recente
-
-
+
Modalità
-
Core UplinkWitness
+
+
+ +
+
+
Modello / firmware
-
-
+
Uptime router
-
-
+
Sessione WAN
-
-
+
WAN fisica
-
-
+
Traffico WAN
-
-
+
Temperatura CPU
-
-
+
Ottica fibra RX
-
Solo se fibra attiva sul FRITZ
+
Ottica fibra TX
-
Solo se fibra attiva sul FRITZ
+
Reconnect delay
-
Router boot → WAN session
+
+
+ +
+
Caricamento incidenti…
+
+ + +
+ From 29d32a6aa940490fbac16528d03d748c0243e499 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:33:33 +0200 Subject: [PATCH 06/26] Document webOS TV packaging and physical deployment --- clients/webos/README.md | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 clients/webos/README.md diff --git a/clients/webos/README.md b/clients/webos/README.md new file mode 100644 index 0000000..8c6c2c4 --- /dev/null +++ b/clients/webos/README.md @@ -0,0 +1,98 @@ +# UplinkWitness TV for LG webOS + +This directory contains the installable launcher shell for the UplinkWitness TV client. + +Primary physical target for the first validation is the LG OLED55E8PLA / webOS TV 4.x generation. The package deliberately uses a small ES5-compatible launcher and opens the TV interface served by the UplinkWitness host at `/wallboard?webos=1`. + +## Why a hosted UI + +The TV package provides the LG launcher integration, icon, persistent server address and fullscreen app container. The actual dashboard is served by the Raspberry Pi / Linux host. + +This keeps monitoring and presentation in one UplinkWitness version, avoids duplicating the diagnostic engine on the television, avoids cross-origin API plumbing, and allows dashboard improvements to arrive with normal UplinkWitness updates without reinstalling the IPK. + +The universal `/wallboard` browser fallback remains available even when the LG package is not installed. + +## Requirements + +On the TV: + +- LG Developer Mode app installed from the LG Content Store +- Developer Mode enabled +- TV and development computer on the same LAN + +On the development computer: + +- Node.js + npm +- current LG webOS CLI (`@webos-tools/cli`) + +Install the CLI: + + npm install -g @webos-tools/cli + ares -V + +## Package + +From the UplinkWitness repository root: + + ares-package ./clients/webos + +The expected package name begins with: + + com.lucaxtech.app.uplinkwitness_0.1.0_ + +## Register the TV + +In the Developer Mode app on the TV, enable Developer Mode and Key Server. Note the TV LAN IP and the six-character passphrase shown by the app. + +Then on the development computer: + + ares-setup-device --add livingroom-tv -i "host=TV_IP" -i "port=9922" -i "username=prisoner" + ares-novacom --device livingroom-tv --getkey + +Enter the passphrase shown on the TV when requested. + +Check connectivity: + + ares-setup-device --list + +## Install and launch + +Replace the package filename with the one produced by `ares-package`: + + ares-install --device livingroom-tv ./com.lucaxtech.app.uplinkwitness_0.1.0_all.ipk + ares-launch --device livingroom-tv com.lucaxtech.app.uplinkwitness + +The first launch asks for the UplinkWitness server URL. Try: + + http://linewatch.local:8080 + +If mDNS is not resolved by the TV, use the Raspberry Pi LAN address instead: + + http://192.168.x.x:8080 + +The server address is stored locally by the TV app and survives normal app restarts. Press the **Yellow** remote key to reopen server settings. + +## TV controls + +Inside the UplinkWitness TV interface: + +- Left / Right: switch Overview, Network, Router, Incidents +- Magic Remote pointer: tabs remain clickable +- Back while inside a secondary section: return to Overview +- Yellow: server settings (handled by the launcher shell when it has focus) + +## Compatibility rules + +The TV UI intentionally avoids: + +- CSS Grid +- JavaScript modules +- arrow functions / modern-only syntax +- external JavaScript or CDN dependencies +- direct monitoring logic on the TV + +The graphics target is 1920x1080, which is the webOS graphics resolution used on UHD TV models. + +## Developer Mode renewal + +Automated session renewal from the Raspberry Pi is intentionally a separate deployment task. First validate that the 0.1.0 app packages, installs, launches and survives a normal restart on the physical OLED55E8PLA. From 13cf5b4fddec10ba336fc74b4e291da40c62e01f Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:33:53 +0200 Subject: [PATCH 07/26] Add regression tests for webOS TV client --- tests/test_webos_client.py | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_webos_client.py diff --git a/tests/test_webos_client.py b/tests/test_webos_client.py new file mode 100644 index 0000000..1b6a948 --- /dev/null +++ b/tests/test_webos_client.py @@ -0,0 +1,51 @@ +import json +import struct +import unittest +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +WEBOS = ROOT / "clients" / "webos" + + +def png_size(path): + data = path.read_bytes() + if data[:8] != b"\x89PNG\r\n\x1a\n": + return None + return struct.unpack(">II", data[16:24]) + + +class WebOSTVClientTests(unittest.TestCase): + def test_appinfo_has_required_webos_metadata(self): + info = json.loads((WEBOS / "appinfo.json").read_text(encoding="utf-8")) + self.assertEqual(info["id"], "com.lucaxtech.app.uplinkwitness") + self.assertEqual(info["type"], "web") + self.assertEqual(info["main"], "index.html") + self.assertEqual(info["version"], "0.1.0") + self.assertEqual(info["resolution"], "1920x1080") + self.assertEqual(info["icon"], "icon.png") + self.assertEqual(info["largeIcon"], "largeicon.png") + + def test_webos_icons_match_lg_test_sizes(self): + self.assertEqual(png_size(WEBOS / "icon.png"), (80, 80)) + self.assertEqual(png_size(WEBOS / "largeicon.png"), (130, 130)) + + def test_launcher_persists_server_and_opens_wallboard(self): + html = (WEBOS / "index.html").read_text(encoding="utf-8") + self.assertIn("uplinkwitness.server", html) + self.assertIn("/wallboard?webos=1", html) + self.assertIn("405", html) # Yellow key + self.assertNotIn("type=\"module\"", html) + + def test_tv_wallboard_uses_legacy_safe_surface(self): + html = (ROOT / "templates" / "wallboard.html").read_text(encoding="utf-8") + for marker in ("Overview", "Network", "Router", "Incidents", "/api/status", "/api/history?hours=24", "/api/events?limit=30"): + self.assertIn(marker, html) + self.assertIn("XMLHttpRequest", html) + self.assertNotIn("display:grid", html) + self.assertNotIn("fetch(", html) + self.assertNotIn("=>", html) + self.assertNotIn("dateStyle", html) + + +if __name__ == "__main__": + unittest.main() From d70a03190e659859b8c27b13358ad98686e59667 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:34:32 +0200 Subject: [PATCH 08/26] Keep webOS launcher compatible with Chromium 53 CSS --- clients/webos/index.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/clients/webos/index.html b/clients/webos/index.html index a288b60..fa6be09 100644 --- a/clients/webos/index.html +++ b/clients/webos/index.html @@ -7,14 +7,14 @@ -
Yellow: server settings
+
@@ -39,7 +40,7 @@
-
Example: http://linewatch.local:8080 or http://192.168.x.x:8080
Use the Yellow remote key at any time to reopen this screen.
+
Example: http://linewatch.local:8080 or http://192.168.x.x:8080
Use Server settings with the Magic Remote if you need to change the host later.
@@ -97,6 +98,7 @@ try{frame.contentWindow.focus();}catch(e){} }); document.getElementById('connect').addEventListener('click',function(){openServer(input.value);}); + document.getElementById('settingsButton').addEventListener('click',function(){showSetup(!!frame.src);}); cancel.addEventListener('click',function(){setup.style.display='none';try{frame.contentWindow.focus();}catch(e){}}); input.addEventListener('keydown',function(e){if((e.keyCode||e.which)===13){openServer(input.value);}}); document.addEventListener('keydown',function(e){ From 1286e97e02aa40280bec874a20d2190dcd553841 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 17:43:06 +0200 Subject: [PATCH 12/26] Tighten webOS physical test instructions --- clients/webos/README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clients/webos/README.md b/clients/webos/README.md index 8c6c2c4..3e47893 100644 --- a/clients/webos/README.md +++ b/clients/webos/README.md @@ -40,6 +40,8 @@ The expected package name begins with: com.lucaxtech.app.uplinkwitness_0.1.0_ +CI also packages the client and publishes an `uplinkwitness-tv-ipk` workflow artifact so the exact tested IPK can be installed without rebuilding it locally. + ## Register the TV In the Developer Mode app on the TV, enable Developer Mode and Key Server. Note the TV LAN IP and the six-character passphrase shown by the app. @@ -57,7 +59,7 @@ Check connectivity: ## Install and launch -Replace the package filename with the one produced by `ares-package`: +Replace the package filename with the one produced by `ares-package` or downloaded from CI: ares-install --device livingroom-tv ./com.lucaxtech.app.uplinkwitness_0.1.0_all.ipk ares-launch --device livingroom-tv com.lucaxtech.app.uplinkwitness @@ -70,7 +72,9 @@ If mDNS is not resolved by the TV, use the Raspberry Pi LAN address instead: http://192.168.x.x:8080 -The server address is stored locally by the TV app and survives normal app restarts. Press the **Yellow** remote key to reopen server settings. +The server address is stored locally by the TV app and survives normal app restarts. + +A small **Server settings** button remains available in the launcher shell for the Magic Remote pointer. The Yellow key is also accepted as a shortcut when the launcher shell itself has keyboard focus; it is not relied on as the only recovery path. ## TV controls @@ -79,7 +83,7 @@ Inside the UplinkWitness TV interface: - Left / Right: switch Overview, Network, Router, Incidents - Magic Remote pointer: tabs remain clickable - Back while inside a secondary section: return to Overview -- Yellow: server settings (handled by the launcher shell when it has focus) +- Magic Remote `Server settings`: reopen the saved-host configuration ## Compatibility rules @@ -91,7 +95,7 @@ The TV UI intentionally avoids: - external JavaScript or CDN dependencies - direct monitoring logic on the TV -The graphics target is 1920x1080, which is the webOS graphics resolution used on UHD TV models. +The graphics target is 1920x1080. ## Developer Mode renewal From 426a9f96879f8e29ab5093865d363247b031fffc Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 20:59:50 +0200 Subject: [PATCH 13/26] fix(webos): remove installation-specific server default --- clients/webos/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/webos/index.html b/clients/webos/index.html index 265940a..4142d38 100644 --- a/clients/webos/index.html +++ b/clients/webos/index.html @@ -34,13 +34,13 @@
UplinkWitness TV
Connect this TV to your UplinkWitness host.
- +
-
Example: http://linewatch.local:8080 or http://192.168.x.x:8080
Use Server settings with the Magic Remote if you need to change the host later.
+
Use the address shown by UplinkWitness after installation, for example http://your-host.local:8080, or the host LAN IP such as http://192.168.x.x:8080.
Use Server settings with the Magic Remote if you need to change the host later.
From 5e704cda94848045f0b6dcdcb788bcef02550960 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:00:04 +0200 Subject: [PATCH 14/26] docs(webos): document host-neutral server configuration --- clients/webos/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clients/webos/README.md b/clients/webos/README.md index 3e47893..e80082c 100644 --- a/clients/webos/README.md +++ b/clients/webos/README.md @@ -64,15 +64,15 @@ Replace the package filename with the one produced by `ares-package` or download ares-install --device livingroom-tv ./com.lucaxtech.app.uplinkwitness_0.1.0_all.ipk ares-launch --device livingroom-tv com.lucaxtech.app.uplinkwitness -The first launch asks for the UplinkWitness server URL. Try: +The first launch asks for the UplinkWitness server URL. Enter the address shown by `install.sh` on the monitored host, for example: - http://linewatch.local:8080 + http://your-host.local:8080 -If mDNS is not resolved by the TV, use the Raspberry Pi LAN address instead: +If mDNS is not resolved by the TV, use the host LAN address instead: http://192.168.x.x:8080 -The server address is stored locally by the TV app and survives normal app restarts. +The launcher intentionally does not ship with an installation-specific hostname prefilled. The server address is stored locally by the TV app after a successful configuration and survives normal app restarts. A small **Server settings** button remains available in the launcher shell for the Magic Remote pointer. The Yellow key is also accepted as a shortcut when the launcher shell itself has keyboard focus; it is not relied on as the only recovery path. From 9c1350a1583ccef835dec37f2abd5264f8781cc4 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:00:15 +0200 Subject: [PATCH 15/26] test(webos): reject installation-specific hostname defaults --- tests/test_webos_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_webos_client.py b/tests/test_webos_client.py index 1b6a948..3dff535 100644 --- a/tests/test_webos_client.py +++ b/tests/test_webos_client.py @@ -34,6 +34,8 @@ def test_launcher_persists_server_and_opens_wallboard(self): self.assertIn("uplinkwitness.server", html) self.assertIn("/wallboard?webos=1", html) self.assertIn("405", html) # Yellow key + self.assertIn('placeholder="http://your-host.local:8080"', html) + self.assertNotIn("linewatch.local", html) self.assertNotIn("type=\"module\"", html) def test_tv_wallboard_uses_legacy_safe_surface(self): From 54a6da3f7c399df809caab76c4a79e8fb065bf01 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:04:52 +0200 Subject: [PATCH 16/26] feat(webos): add Developer Mode renewal helper --- clients/webos/renew-devmode.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 clients/webos/renew-devmode.sh diff --git a/clients/webos/renew-devmode.sh b/clients/webos/renew-devmode.sh new file mode 100644 index 0000000..8e8289e --- /dev/null +++ b/clients/webos/renew-devmode.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +DEVICE="${1:-${WEBOS_DEVICE:-}}" + +if [ -z "$DEVICE" ]; then + echo "Usage: $0 DEVICE_NAME" >&2 + echo "Example: $0 livingroom-tv" >&2 + exit 2 +fi + +ARES_LAUNCH_BIN="${ARES_LAUNCH_BIN:-}" +if [ -z "$ARES_LAUNCH_BIN" ]; then + ARES_LAUNCH_BIN="$(command -v ares-launch || true)" +fi + +if [ -z "$ARES_LAUNCH_BIN" ] || [ ! -x "$ARES_LAUNCH_BIN" ]; then + echo "ares-launch was not found. Install @webos-tools/cli first." >&2 + exit 127 +fi + +echo "Renewing LG webOS Developer Mode session for device '$DEVICE'..." +"$ARES_LAUNCH_BIN" --device "$DEVICE" com.palmdts.devmode -p "extend=true" +echo "Developer Mode renewal request completed for '$DEVICE'." From f2de7d4653c29a26b665b3798f431f1ef7544222 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:05:07 +0200 Subject: [PATCH 17/26] feat(webos): add systemd renewal timer installer --- clients/webos/install-renewal-timer.sh | 83 ++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 clients/webos/install-renewal-timer.sh diff --git a/clients/webos/install-renewal-timer.sh b/clients/webos/install-renewal-timer.sh new file mode 100644 index 0000000..ded0df9 --- /dev/null +++ b/clients/webos/install-renewal-timer.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ "$(id -u)" -eq 0 ]; then + echo "Run this installer as your normal user, not with sudo." >&2 + exit 1 +fi + +DEVICE="${1:-}" +if [ -z "$DEVICE" ]; then + echo "Usage: $0 DEVICE_NAME" >&2 + echo "Example: $0 livingroom-tv" >&2 + exit 2 +fi + +if ! command -v ares-launch >/dev/null 2>&1; then + echo "ares-launch was not found. Install @webos-tools/cli first." >&2 + exit 127 +fi + +if ! command -v ares-setup-device >/dev/null 2>&1; then + echo "ares-setup-device was not found. Install @webos-tools/cli first." >&2 + exit 127 +fi + +if ! ares-setup-device --list | awk -v device="$DEVICE" 'NR > 2 && $1 == device { found=1 } END { exit !found }'; then + echo "webOS device '$DEVICE' is not registered for this user." >&2 + echo "Register it with ares-setup-device and retrieve its key with ares-novacom --getkey first." >&2 + exit 3 +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +USER_NAME="$(id -un)" +USER_HOME="${HOME:?HOME is not set}" +ARES_LAUNCH_BIN="$(command -v ares-launch)" +INSTALL_DIR="/usr/local/lib/uplinkwitness" +SERVICE_NAME="uplinkwitness-webos-renew.service" +TIMER_NAME="uplinkwitness-webos-renew.timer" + +echo "Testing Developer Mode renewal before installing the timer..." +ARES_LAUNCH_BIN="$ARES_LAUNCH_BIN" "$SCRIPT_DIR/renew-devmode.sh" "$DEVICE" + +sudo install -d -m 0755 "$INSTALL_DIR" +sudo install -m 0755 "$SCRIPT_DIR/renew-devmode.sh" "$INSTALL_DIR/renew-webos-devmode" + +sudo tee "/etc/systemd/system/$SERVICE_NAME" >/dev/null </dev/null < Date: Sun, 6 Sep 2026 21:05:25 +0200 Subject: [PATCH 18/26] ci(webos): validate renewal helper scripts --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8644666..ab48895 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: - name: Run unit tests run: python -m unittest discover -s tests -v - name: Check shell scripts - run: bash -n install.sh configure.sh run_monitor.sh run_dashboard.sh + run: bash -n install.sh configure.sh run_monitor.sh run_dashboard.sh clients/webos/renew-devmode.sh clients/webos/install-renewal-timer.sh webos-package: runs-on: ubuntu-latest From 4175a86103c7f192da01bda6829299106ffc04ec Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:05:42 +0200 Subject: [PATCH 19/26] docs(webos): document automatic Developer Mode renewal --- clients/webos/README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/clients/webos/README.md b/clients/webos/README.md index e80082c..e04e9c1 100644 --- a/clients/webos/README.md +++ b/clients/webos/README.md @@ -99,4 +99,22 @@ The graphics target is 1920x1080. ## Developer Mode renewal -Automated session renewal from the Raspberry Pi is intentionally a separate deployment task. First validate that the 0.1.0 app packages, installs, launches and survives a normal restart on the physical OLED55E8PLA. +LG Developer Mode sessions expire unless they are extended. With the current `@webos-tools/cli`, renewal is performed by launching the Developer Mode app with the extension parameter: + + ares-launch --device livingroom-tv com.palmdts.devmode -p "extend=true" + +This command has been physically validated on the OLED55E8PLA and returned the displayed remaining session to approximately 1000 hours. + +For an always-on Raspberry Pi or Linux host, first register the TV and retrieve its key for the same Linux user that runs UplinkWitness. Then install the included weekly systemd timer: + + ./clients/webos/install-renewal-timer.sh livingroom-tv + +The installer performs one real renewal before installing anything, copies the renewal helper to `/usr/local/lib/uplinkwitness/`, and enables `uplinkwitness-webos-renew.timer`. The timer renews approximately once per week and also schedules an attempt after the Linux host boots. + +Useful checks: + + systemctl list-timers uplinkwitness-webos-renew.timer + sudo systemctl start uplinkwitness-webos-renew.service + journalctl -u uplinkwitness-webos-renew.service + +The timer contains no TV IP address or host-specific UplinkWitness URL. It uses the webOS device alias already registered by `ares-setup-device` for the Linux user. From 4acd38ba6a651a3b442080c5c41b0d43b6c53c11 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:05:56 +0200 Subject: [PATCH 20/26] test(webos): cover Developer Mode renewal automation --- tests/test_webos_client.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_webos_client.py b/tests/test_webos_client.py index 3dff535..9a588ea 100644 --- a/tests/test_webos_client.py +++ b/tests/test_webos_client.py @@ -38,6 +38,15 @@ def test_launcher_persists_server_and_opens_wallboard(self): self.assertNotIn("linewatch.local", html) self.assertNotIn("type=\"module\"", html) + def test_devmode_renewal_uses_current_cli_launch_flow(self): + script = (WEBOS / "renew-devmode.sh").read_text(encoding="utf-8") + installer = (WEBOS / "install-renewal-timer.sh").read_text(encoding="utf-8") + self.assertIn("com.palmdts.devmode", script) + self.assertIn('extend=true', script) + self.assertIn("uplinkwitness-webos-renew.timer", installer) + self.assertIn("OnUnitActiveSec=7d", installer) + self.assertNotIn("192.168.", script + installer) + def test_tv_wallboard_uses_legacy_safe_surface(self): html = (ROOT / "templates" / "wallboard.html").read_text(encoding="utf-8") for marker in ("Overview", "Network", "Router", "Incidents", "/api/status", "/api/history?hours=24", "/api/events?limit=30"): From 55228e9660e75aef385f3b6abc7229fc264eaee4 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:10:13 +0200 Subject: [PATCH 21/26] fix(webos): mark renewal scripts executable --- clients/webos/install-renewal-timer.sh | 0 clients/webos/renew-devmode.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 clients/webos/install-renewal-timer.sh mode change 100644 => 100755 clients/webos/renew-devmode.sh diff --git a/clients/webos/install-renewal-timer.sh b/clients/webos/install-renewal-timer.sh old mode 100644 new mode 100755 diff --git a/clients/webos/renew-devmode.sh b/clients/webos/renew-devmode.sh old mode 100644 new mode 100755 From 9f84a508be64aebbbf69351da0fdbf4afd4ac24f Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:32:23 +0200 Subject: [PATCH 22/26] feat(webos): accept launch-time server configuration --- clients/webos/appinfo.json | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/webos/appinfo.json b/clients/webos/appinfo.json index 2cba3bb..212bdcf 100644 --- a/clients/webos/appinfo.json +++ b/clients/webos/appinfo.json @@ -10,5 +10,6 @@ "largeIcon": "largeicon.png", "iconColor": "#111827", "resolution": "1920x1080", + "handlesRelaunch": true, "transparent": false } From 6ec947e98e55b95693b041498dd963cc92e031f6 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:32:49 +0200 Subject: [PATCH 23/26] feat(webos): configure server via launch params --- clients/webos/index.html | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/clients/webos/index.html b/clients/webos/index.html index 4142d38..f962a98 100644 --- a/clients/webos/index.html +++ b/clients/webos/index.html @@ -91,6 +91,29 @@ frame.src=server+'/wallboard?webos=1'; timer=setTimeout(function(){loading.style.display='none';},7000); } + function activateApp(){ + try{ + if(window.PalmSystem && PalmSystem.activate){PalmSystem.activate();} + else if(window.webOSSystem && webOSSystem.activate){webOSSystem.activate();} + }catch(e){} + } + function handleLaunch(data){ + var detail=data && data.detail ? data.detail : {}; + if(detail.server){openServer(detail.server);} + activateApp(); + } + function initialLaunchServer(){ + try{ + if(window.PalmSystem && PalmSystem.launchParams){ + var params=JSON.parse(PalmSystem.launchParams); + if(params && params.server){return params.server;} + } + }catch(e){} + return ''; + } + + document.addEventListener('webOSLaunch',handleLaunch,true); + document.addEventListener('webOSRelaunch',handleLaunch,true); frame.addEventListener('load',function(){ if(timer){clearTimeout(timer);timer=null;} loading.style.display='none'; @@ -107,8 +130,11 @@ if(code===461 && setup.style.display==='flex' && frame.src){e.preventDefault();setup.style.display='none';try{frame.contentWindow.focus();}catch(x){}} }); + var launchServer=initialLaunchServer(); var saved=localStorage.getItem(KEY); - if(saved){openServer(saved);}else{showSetup(false);} + if(launchServer){openServer(launchServer);} + else if(saved){openServer(saved);} + else{showSetup(false);} })(); From dd1cecdf476ea5f7a7b1ab925c86afa8f38eca2f Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:33:06 +0200 Subject: [PATCH 24/26] test(webos): cover launch-param server setup --- tests/test_webos_client.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_webos_client.py b/tests/test_webos_client.py index 9a588ea..55815d1 100644 --- a/tests/test_webos_client.py +++ b/tests/test_webos_client.py @@ -24,6 +24,7 @@ def test_appinfo_has_required_webos_metadata(self): self.assertEqual(info["resolution"], "1920x1080") self.assertEqual(info["icon"], "icon.png") self.assertEqual(info["largeIcon"], "largeicon.png") + self.assertTrue(info["handlesRelaunch"]) def test_webos_icons_match_lg_test_sizes(self): self.assertEqual(png_size(WEBOS / "icon.png"), (80, 80)) @@ -38,6 +39,14 @@ def test_launcher_persists_server_and_opens_wallboard(self): self.assertNotIn("linewatch.local", html) self.assertNotIn("type=\"module\"", html) + def test_launcher_accepts_server_launch_parameter(self): + html = (WEBOS / "index.html").read_text(encoding="utf-8") + self.assertIn("webOSLaunch", html) + self.assertIn("webOSRelaunch", html) + self.assertIn("PalmSystem.launchParams", html) + self.assertIn("detail.server", html) + self.assertIn("openServer(detail.server)", html) + def test_devmode_renewal_uses_current_cli_launch_flow(self): script = (WEBOS / "renew-devmode.sh").read_text(encoding="utf-8") installer = (WEBOS / "install-renewal-timer.sh").read_text(encoding="utf-8") From 4afdb85874e96efa5380fd2f929ba0d82d534029 Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:43:42 +0200 Subject: [PATCH 25/26] fix(webos): remove tiny overview vertical overflow --- templates/wallboard.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates/wallboard.html b/templates/wallboard.html index 8e1d4fe..80651d0 100644 --- a/templates/wallboard.html +++ b/templates/wallboard.html @@ -27,6 +27,9 @@ .statusGood{color:#4bd37b}.statusBad{color:#ff8c8c}.statusMuted{color:#93a4bd} .events{border:2px solid #243149;border-radius:18px;background:#111827;overflow:hidden}.event{display:flex;align-items:center;min-height:76px;padding:14px 20px;border-bottom:1px solid #243149}.event:last-child{border-bottom:0}.eventType{width:44%;font-size:22px;font-weight:700}.eventTime{width:36%;color:#cbd5e1;font-size:18px}.eventDuration{width:20%;text-align:right;color:#93a4bd;font-size:18px}.empty{padding:40px;color:#93a4bd;font-size:22px;text-align:center} .footer{display:flex;justify-content:space-between;margin-top:18px;color:#697b96;font-size:15px}.keys{color:#93a4bd} +html.webos .app{padding-bottom:18px} +html.webos #view-overview{padding-top:18px} +html.webos #view-overview .split{margin-top:8px} @media(max-width:900px){.app{padding:16px}.topbar{display:block}.clock{text-align:left;margin-top:12px}.nav{overflow-x:auto}.nav button{min-width:145px;font-size:18px}.state{font-size:34px}.heroMeta{display:none}.card{width:50%}.split{display:block}.panelWrap.wide,.panelWrap.narrow{width:100%}.footer{display:none}} @@ -100,6 +103,7 @@ - + \ No newline at end of file From 66cd19252ac3037be3fcf1966d46f0d13d20308f Mon Sep 17 00:00:00 2001 From: Luca Serioli Date: Sun, 6 Sep 2026 21:44:05 +0200 Subject: [PATCH 26/26] test(webos): cover overview overflow guard --- tests/test_webos_client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_webos_client.py b/tests/test_webos_client.py index 55815d1..d93e8fc 100644 --- a/tests/test_webos_client.py +++ b/tests/test_webos_client.py @@ -61,6 +61,9 @@ def test_tv_wallboard_uses_legacy_safe_surface(self): for marker in ("Overview", "Network", "Router", "Incidents", "/api/status", "/api/history?hours=24", "/api/events?limit=30"): self.assertIn(marker, html) self.assertIn("XMLHttpRequest", html) + self.assertIn("webos=1", html) + self.assertIn("html.webos #view-overview", html) + self.assertIn("padding-bottom:18px", html) self.assertNotIn("display:grid", html) self.assertNotIn("fetch(", html) self.assertNotIn("=>", html)