Polish the snapshot wall and add timed refresh

This commit is contained in:
OpenClaw
2026-04-19 01:53:48 +00:00
parent f818a8da73
commit 22ba58f129
4 changed files with 74 additions and 48 deletions

Binary file not shown.

View File

@@ -2,6 +2,7 @@ sub init()
m.top.setFocus(true) m.top.setFocus(true)
m.backend = m.top.findNode("backend") m.backend = m.top.findNode("backend")
m.status = m.top.findNode("status") m.status = m.top.findNode("status")
m.refreshTimer = m.top.findNode("refreshTimer")
m.cameraPosters = [ m.cameraPosters = [
m.top.findNode("poster1"), m.top.findNode("poster1"),
m.top.findNode("poster2"), m.top.findNode("poster2"),
@@ -14,6 +15,7 @@ sub init()
m.top.findNode("cam3"), m.top.findNode("cam3"),
m.top.findNode("cam4") m.top.findNode("cam4")
] ]
m.refreshToken = 0
loadConfig() loadConfig()
end sub end sub
@@ -27,8 +29,10 @@ sub loadConfig()
return return
end if end if
m.config = config
if config.doesExist("backend") and config.backend.doesExist("baseUrl") then if config.doesExist("backend") and config.backend.doesExist("baseUrl") then
m.backend.text = "Frigate: " + config.backend.baseUrl m.backend.text = "Frigate snapshot source"
end if end if
if not config.doesExist("cameras") then if not config.doesExist("cameras") then
@@ -36,8 +40,23 @@ sub loadConfig()
return return
end if end if
cameras = config.cameras if config.doesExist("backend") and config.backend.doesExist("refreshSeconds") then
m.status.text = cameras.count().toStr() + " cameras loaded, snapshot-first prototype" m.refreshTimer.duration = config.backend.refreshSeconds
end if
m.refreshTimer.observeField("fire", "onRefreshTimerFire")
m.refreshTimer.control = "start"
renderCameras()
end sub
sub renderCameras()
if m.config = invalid or not m.config.doesExist("cameras") then
return
end if
cameras = m.config.cameras
m.status.text = cameras.count().toStr() + " cameras loaded, refreshing every " + m.refreshTimer.duration.toStr() + "s"
for i = 0 to m.cameraNodes.count() - 1 for i = 0 to m.cameraNodes.count() - 1
poster = m.cameraPosters[i] poster = m.cameraPosters[i]
@@ -45,18 +64,8 @@ sub loadConfig()
if i < cameras.count() then if i < cameras.count() then
camera = cameras[i] camera = cameras[i]
label = camera.name poster.uri = addCacheBust(buildSnapshotUrl(m.config, camera))
node.text = camera.name
if camera.doesExist("host") then
label = label + chr(10) + camera.host
end if
if camera.doesExist("frigateCamera") then
label = label + chr(10) + "Frigate ID: " + camera.frigateCamera
end if
poster.uri = buildSnapshotUrl(config, camera)
node.text = label
else else
poster.uri = "" poster.uri = ""
node.text = "Empty slot" node.text = "Empty slot"
@@ -64,6 +73,11 @@ sub loadConfig()
end for end for
end sub end sub
sub onRefreshTimerFire()
m.refreshToken = m.refreshToken + 1
renderCameras()
end sub
function buildSnapshotUrl(config as Object, camera as Object) as String function buildSnapshotUrl(config as Object, camera as Object) as String
if not config.doesExist("backend") or not config.backend.doesExist("baseUrl") then if not config.doesExist("backend") or not config.backend.doesExist("baseUrl") then
return "" return ""
@@ -88,3 +102,13 @@ function buildSnapshotUrl(config as Object, camera as Object) as String
return baseUrl + path return baseUrl + path
end function end function
function addCacheBust(url as String) as String
token = m.refreshToken.toStr()
if instr(1, url, "?") > 0 then
return url + "&cb=" + token
end if
return url + "?cb=" + token
end function

View File

@@ -6,7 +6,7 @@
<Rectangle id="bg" color="0x101418FF" width="1920" height="1080" /> <Rectangle id="bg" color="0x101418FF" width="1920" height="1080" />
<Label <Label
id="title" id="title"
translation="[80, 80]" translation="[70, 55]"
width="1760" width="1760"
height="60" height="60"
text="visionLabs" text="visionLabs"
@@ -14,81 +14,82 @@
color="0xFFFFFFFF" /> color="0xFFFFFFFF" />
<Label <Label
id="subtitle" id="subtitle"
translation="[80, 150]" translation="[70, 108]"
width="1760" width="1760"
height="40" height="40"
text="Labs camera viewer for Roku" text="Snapshot wall for Roku"
font="font:MediumSystemFont" font="font:MediumSystemFont"
color="0xC9D1D9FF" /> color="0xC9D1D9FF" />
<Label <Label
id="backend" id="backend"
translation="[80, 210]" translation="[70, 145]"
width="1760" width="1760"
height="40" height="40"
text="Frigate: not loaded" text="Direct Frigate snapshots"
font="font:SmallSystemFont" font="font:SmallSystemFont"
color="0x8B949EFF" /> color="0x8B949EFF" />
<Label <Label
id="status" id="status"
translation="[80, 260]" translation="[70, 175]"
width="1760" width="1760"
height="60" height="40"
text="Loading camera config..." text="Loading camera wall..."
font="font:MediumSystemFont" font="font:SmallSystemFont"
color="0x8B949EFF" /> color="0x8B949EFF" />
<Timer id="refreshTimer" repeat="true" duration="15" />
<Poster <Poster
id="poster1" id="poster1"
translation="[80, 360]" translation="[70, 230]"
width="760" width="820"
height="300" height="280"
loadDisplayMode="scaleToFit" /> loadDisplayMode="scaleToFit" />
<Label <Label
id="cam1" id="cam1"
translation="[80, 675]" translation="[70, 520]"
width="760" width="820"
height="70" height="40"
text="Cam slot 1" text="Cam slot 1"
font="font:MediumSystemFont" font="font:MediumSystemFont"
color="0xFFFFFFFF" /> color="0xFFFFFFFF" />
<Poster <Poster
id="poster2" id="poster2"
translation="[980, 360]" translation="[1030, 230]"
width="760" width="820"
height="300" height="280"
loadDisplayMode="scaleToFit" /> loadDisplayMode="scaleToFit" />
<Label <Label
id="cam2" id="cam2"
translation="[980, 675]" translation="[1030, 520]"
width="760" width="820"
height="70" height="40"
text="Cam slot 2" text="Cam slot 2"
font="font:MediumSystemFont" font="font:MediumSystemFont"
color="0xFFFFFFFF" /> color="0xFFFFFFFF" />
<Poster <Poster
id="poster3" id="poster3"
translation="[80, 760]" translation="[70, 590]"
width="760" width="820"
height="300" height="280"
loadDisplayMode="scaleToFit" /> loadDisplayMode="scaleToFit" />
<Label <Label
id="cam3" id="cam3"
translation="[80, 1015]" translation="[70, 880]"
width="760" width="820"
height="50" height="40"
text="Cam slot 3" text="Cam slot 3"
font="font:MediumSystemFont" font="font:MediumSystemFont"
color="0xFFFFFFFF" /> color="0xFFFFFFFF" />
<Poster <Poster
id="poster4" id="poster4"
translation="[980, 760]" translation="[1030, 590]"
width="760" width="820"
height="300" height="280"
loadDisplayMode="scaleToFit" /> loadDisplayMode="scaleToFit" />
<Label <Label
id="cam4" id="cam4"
translation="[980, 1015]" translation="[1030, 880]"
width="760" width="820"
height="50" height="40"
text="Cam slot 4" text="Cam slot 4"
font="font:MediumSystemFont" font="font:MediumSystemFont"
color="0xFFFFFFFF" /> color="0xFFFFFFFF" />

View File

@@ -4,6 +4,7 @@
"type": "frigate", "type": "frigate",
"baseUrl": "http://192.168.0.223:5000/", "baseUrl": "http://192.168.0.223:5000/",
"mode": "snapshotFirst", "mode": "snapshotFirst",
"refreshSeconds": 15,
"notes": "Keep raw camera credentials in Frigate or go2rtc, not in the Roku repo." "notes": "Keep raw camera credentials in Frigate or go2rtc, not in the Roku repo."
}, },
"layout": { "layout": {