From 22ba58f129d11ea7e6fa5656cdaafc4bfb22b536 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Sun, 19 Apr 2026 01:53:48 +0000 Subject: [PATCH] Polish the snapshot wall and add timed refresh --- builds/visionLabs-roku-smoketest.zip | Bin 2505 -> 2714 bytes components/MainScene.brs | 54 +++++++++++++++------ components/MainScene.xml | 67 ++++++++++++++------------- config/cameras.json | 1 + 4 files changed, 74 insertions(+), 48 deletions(-) diff --git a/builds/visionLabs-roku-smoketest.zip b/builds/visionLabs-roku-smoketest.zip index bfd6042627ea41887a45549dbcb6e82fc448e321..4ba66b72611d8cddb3a730debf804479f22bf059 100644 GIT binary patch delta 2090 zcmY+FcR1S%7sq1-r9#E3P3#zTkNs<)^ZwRh3$A~aFF zR;&hzS=25rYE;b{#nt;h@B2JG=a2K8^L+pNp3gZn{&a!LU?!lHhj>$iB3;as|IDfFlNR3t4WqY`X1r_c)u=-) zvR+wRxjTMc@F+yqF-ZCvechUi(qA_Y7u-`IJCpz(O&e2ZE0=GkW>$oF`tAjt_}cDP zCabs#L7dJ=PB?xYLtxG~=1Ze4);_1-_HcE2FQ%xVfP11GQci|z|MH!ak?f(K^h!0R z#HP5I+FY&QxouHt&eCQ)FeVJ6dAxhw9M}~?vfT{jyCkR?2tj#&A(z0Gx^d;^u!q#X zs2bp~6LAlNWQL>2Z}} zak^aVYqrtH&(BoUI_nuDh~Af1uYFKjt%IV}!SSUTY4Y;n^XRD1NW|~2yL{*ZeR6p= z6Nb5D-K=qAI|!QAQ56kSM!9f5Wzu&+0_TbgG!dawGWYLqw4Hb5D?P9Pce`!-TRI!) zSQ@z`=SGr238tX{!~O$gSYvD(@)fqqzai8pc^fqwTbXE=wr4B~col!FU(Z4rXB zwP?~WX=QD^+5eI2L=o^W>kMWQCs8M28qD{MVy4o_5Rg{}k?5PIM3lWND}6r!xR92Q zp@8gcg~}$VsXT|(pC4i-53aXwFqqd(*HwD?Er0zx>BwMaB!Yl$J?=~T5`zW4(*1Mag4FlYP2P+RzkXvaV>nPl&{X1H*V#9j~kK#{9VSo}^EmC+P zYxv7WDqmBliVG>FVm0aDkS3>-{wLUttR&&o^p$^SZ4rzyuy?-EH0Nrjjx+&SsLoxgjJDwOiy7 zM@&|xEUeH|WH~@TK>kKIWN0#jKo-P2Rw=-)1wVUA>y|)DuPRI$e*Id>P?a{}t#X6y zBfoP*2V}69j_UU#3+5sdq4Zp=?c!+nL)a23P(Mok{H!B7CJED)2r^Paljd#M zM#n}|*2Y|gJ+?c}5t-oAOgBaT+H7TPmR;JF9?mpOlB9fi<8j^_*?zX#+R(?s%zU_e zv`)XEfBkj$qpl6epCMnfkENG8@pDe)?cC)XynweZjD3TYuB5_Cxf7^k{k` zJ2UlYInpN8S|PW2tk!t4z&JG$dWi^b;A1f57+CtdWE(c*a;j|Ey^lZc$=;uoGJ?6H zIw>;2y~Df6SrJTMnc|aUg=m=ug_*Yl-C`xaPgiyK%1^2bx}sm%)W}ExZX6po6K_T4 z3VT15ArBmdpES*cc_-v|lq{K8XMF(w0aaBbu%eSS3KDJB2xA2cCrz+ITY%4Q$h-c< z1)PsKt|!2Pci~|U57H0eSvTWVpTNm&D$Z|_gMDu%geA-lD;qItZ6#87Ud|f_ysO)7 zavhoDUp7_Etgspz{_12lv}Co|zT?-ZAMbv9WZym)eLFe(fxBw-bnesfyv1_;7d%0( zHzD6;1IOt)Q_(mY)~Ohp>tcV*-KVh|6Fx!0k3w1KK`)1kLLmxcYH*$i5}@ z#f4vYwL0EWNc20zgu>XE>9Ff&#n@raMppk`8f|}a5|jse@-RA`6$33R&Ck?rFbRbAkGcS1wusc@XPNsTkFm3mAgCbt6Pk2f6 zJ43ck8yw0j+v!>=Ys02(6pZgOx7q5HwO!@kFOIn!jniuyG+hyh>XSK&()7EPdp&t^ zSz*XdX*$P{T3;8kLXlSah@*Y%beAXR1(?)2=Yt71^l^FQ$g1v}rYe#-w{I3%^ky1Z_&-LFAetHr9jkg3fHlhQk{J*0Ao5>0S@&8Y8THg{<*oo_40YNKM SHt>%*+iB{Zx^Bq%qx%Owo8VXg delta 1891 zcmY+FdpOgJAIFE$CVuWS+KC)B+1xLaqqS-rbIUE)_3IJCn7fpi?{Vw6g#ETAVJa$8 zDGD*y&7CQigfTLOI4*IlqMUxe^ZcIQug^d4=lMME=l##;^LpNeD!ociK_EB?1oDpvi;M^-g~!GiIQo&p-TX=6B>fA~F|4zWXZSez<{i&Ri8a=d zwtS@&I|a!H$DQ3fpiy4@<$(4TAGQ$ojT+RttAf1q?l+~EMK>`f5l0$%P*nuoRO^$d zFqvY^o3(*GcEZ)7;n6-olc5XUUasH=VAEf#Z_)a;_JN2L(}G4*mMTXlo&ouspeR@@yUgyHMzF1+ z!-we2thNNM;y%0HPESQt!p+KXHRIw@4EAqUjZ<-_ndBI>q*O+IH zw#L#V=g+8Xn?0cx+*C3O*@FuGB-%asYJLavgnYwKd0WQ>)AHzS;Cy`l@<&0xgs5F3 zJzb8KYsGU;-9@tp`$Blz$V_3g-`w&WDM4n{;VsRVmWlZIZDdV|wHPsrIsona7W8dE z8UmPYMYwJ%)Td?h-kw`CPzdh=ecbS$H@9-fB2q6c-bh^4GE=UrikfbiQgHM7@Jol& zNCL2CinsA2?%jFS7D71CGF5(}Y=o3^-Yno{&WS5OQ&-kxT{+IawuuI?0On*`^;bfL zw`Araqsu4WT+CiRHR_ONu$_C;G2}jL+%eQBNhN6W3YR!%$XCjS-&a#^NUhaD#Xk9C z(ZJbW0<8R<1_&s+iB62FwSg*qXGD%+EvX6QJLXLq%H!5{rTZ6PidZGQ7x~6}uxss> z$Dgwnr=Lcy^u&365?#C<&NkUS=y@xm^oQ~oY5F7Bv-L>TGe14I<0w-5``Ozj|arM;+K0xM$ ziQyF?oS9|)!i&W9VENR4c*Mh#ulWeOuxjUwOLXOTrr57g&ZqHRV%4S`!c_$(u@aJS zU}lq@B;O5ht{z3}d5qT)?JEZBO$^rK8kUJ}bkLLX*A>y~fflo6PhIfAP6MkoN&5sC z<;y^BJt7ye)htZsZfFQ!Pve2hJY9)8d1_N5jZ@{(%023$W@TS8SU-t7$RFPjd=Z}m zgz9$Zj(8@2X)YqLkpz?tK1}Io%9xA*=WUz*&v%tp+1S+U%m&8J;o?t>b<5SXSIjV9 z)i{2uQXGYS3l7&hBs)jHODpMQEu_UD!`2aTTn$ej^N#D2P=2c$fH|Xgi<*Kkbvp_} zhng4NQb|<*T>czz+cQ#SKV3^%El$PuPiorRrUaz9}FbtB*Z!J(Wq1{HummM|Eb zO*>+z#2_2uX3#*LacApe30Ln)U-j@gRd)o985R(ExV1EsDs4TS(n2)WT0kN(T|9$BU$~{v@M*;j}7Jocr z46m-M#FE1lAnGinHS;rmY28=3+_N}rv2VPVl2P>2)n+tV!~Tm#|G25wd&V+)Ne_)( za)Kd{H@q!5r2J7+LsWCuFkvZZgCMGzCCK$RMBY4XBpZ2!_0H+|FhDMp0!VW}9%zqfw@s;YFw diff --git a/components/MainScene.brs b/components/MainScene.brs index d2a4ec6..6603443 100644 --- a/components/MainScene.brs +++ b/components/MainScene.brs @@ -2,6 +2,7 @@ sub init() m.top.setFocus(true) m.backend = m.top.findNode("backend") m.status = m.top.findNode("status") + m.refreshTimer = m.top.findNode("refreshTimer") m.cameraPosters = [ m.top.findNode("poster1"), m.top.findNode("poster2"), @@ -14,6 +15,7 @@ sub init() m.top.findNode("cam3"), m.top.findNode("cam4") ] + m.refreshToken = 0 loadConfig() end sub @@ -27,8 +29,10 @@ sub loadConfig() return end if + m.config = config + 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 if not config.doesExist("cameras") then @@ -36,8 +40,23 @@ sub loadConfig() return end if - cameras = config.cameras - m.status.text = cameras.count().toStr() + " cameras loaded, snapshot-first prototype" + if config.doesExist("backend") and config.backend.doesExist("refreshSeconds") then + 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 poster = m.cameraPosters[i] @@ -45,18 +64,8 @@ sub loadConfig() if i < cameras.count() then camera = cameras[i] - label = 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 + poster.uri = addCacheBust(buildSnapshotUrl(m.config, camera)) + node.text = camera.name else poster.uri = "" node.text = "Empty slot" @@ -64,6 +73,11 @@ sub loadConfig() end for end sub +sub onRefreshTimerFire() + m.refreshToken = m.refreshToken + 1 + renderCameras() +end sub + function buildSnapshotUrl(config as Object, camera as Object) as String if not config.doesExist("backend") or not config.backend.doesExist("baseUrl") then return "" @@ -88,3 +102,13 @@ function buildSnapshotUrl(config as Object, camera as Object) as String return baseUrl + path 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 diff --git a/components/MainScene.xml b/components/MainScene.xml index 677304e..18b7637 100644 --- a/components/MainScene.xml +++ b/components/MainScene.xml @@ -6,7 +6,7 @@