From the first proof of concept to today — newest first. Built in a nine-day sprint; versions were assigned to capability milestones afterwards.
A recording is now visible only to its owner, their team admin, and the superadmin — a superadmin’s clips to nobody else. A per-clip share switch opens one recording to the owner’s teammates, and every path that carries footage (list, poster, stream, export, key-track) enforces the same rule. The tab itself grew ownership chips, the ★ keep star, per-row and bulk delete — deletes vanish instantly and finish in the background.
The library’s action bar now serves both worlds: it installs or debugs a freshly dropped file just like a stored package (rename and delete politely grey out for files not yet in the library). And the DevTools debugging flow wears the same bar — Ladit runs the pipeline, Instalovat hops over to the install tab with your source preselected.
A staged upload and a selected library package could both look chosen at once. Now the tab holds a single intent: dropping a file clears the library selection, and picking from the library quietly discards the staged file.
Uploading a package while adding the debug module now uses the same drag-and-drop zone as the install tab — drop a file under "NEBO", it becomes a selectable source with a save-to-library switch in the familiar style.
A switched-off install option no longer melts into its background — the track got a proper grey in both themes.
The install options' switches shrank to the mockup's petite size — no more oversized toggles crowding out their own labels.
The install options got their explanations back — short, one line, never wrapping — with the exact proportions of the approved mockup. Third try's the charm.
The two install switches now sit in one tidy line each — shorter labels, smaller icons, explanations tucked into tooltips. Same choices, half the visual weight.
The install tab rests as a single clean drop area. The moment you pick a package, it turns into a tidy card with the file, two clearly explained switches — launch right away, keep in the library — and one Install button. No more settings hanging around with nothing to apply to.
The install tab and the debugging flow now share a single package library — the same clean selectable rows everywhere, with one action bar: install, debug, rename, delete. Each package wears its owner's colour: green for yours, blue for your team's, amber for the superadmin's.
Uploading a package while adding the debug module now offers the same "save to library" switch the install tab has — the clean original is stored, so next time it's one click away on any TV.
Re-opening the debugging offer no longer routes through a card asking for the same click twice — it goes straight to picking the package. And the footnote under DevTools dropped a cryptic acronym nobody needed to know.
Opening DevTools in a separate window now closes the embedded panel and says where to continue, so two inspectors never wrestle over one TV. The header learned to read the app's real name straight from the package, the add-debug flow openly says when a package already carries the debug module, and a redundant status line above the cards is gone.
The DevTools header now names the app actually running on the TV instead of an internal code name, a new ✕ button lets you disconnect the inspector (and reconnect with one click), and the widescreen layout lost its mystery gap — the camera stretches, the remote compacts, and the tools sit right where they should.
Adding a debug module restarts the app on the TV — and the page now patiently waits for it to call home instead of giving up after the first look. The connect step keeps spinning until the app reports in; only after a good minute does it offer a retry. Plus clearer wording on the upload row and the package library sorted newest-first.
The install tab stopped lecturing about signing certificates — that all happens quietly in the background. What stayed, front and centre, is the one note that matters: an installed app is temporary and uninstalls itself once you release the device.
A small reshuffle on the device detail: the Instalace tab now sits right after Informace — installing apps became an everyday action for everyone, so it earned the spot before the debugging tools.
Deleting a stored package now asks for confirmation, the rename field grew to fit longer names, and the DevTools debugging flow can take its package straight from the library — the same one the install tab uses, with the same sharing rules.
Package sharing now mirrors how the team works: everyone gets the superadmin's packages, a team admin sees the whole team's, and a member sees their own plus what their admins share — a colleague's private uploads stay private. Packages can also be renamed in place, with the same common-sense rules about whose you may touch.
A quick follow-up: right after opening installs to everyone, members still saw "certificates not set up" on devices where the superadmin had set them — the readiness check itself was locked to superadmins. Now everyone sees whether a device is ready to install, while certificate details stay with the superadmin.
Installing an app onto a TV is no longer a superadmin privilege — anyone drops in a package and it's signed and installed automatically. The twist: apps installed by regular users and team admins are temporary. Release the device and the TV uninstalls them by itself, debug builds included. And a new package library keeps your uploads around — your own stay private, a team admin's are shared with the team, a superadmin's with everyone.
A tiny cosmetic fix on the device info card: for regular members, the last visible row no longer draws a divider line into the empty space where the admin-only rows would be.
The three roles got honest names — Superadmin runs the infrastructure, a Team admin (Správce týmu) manages their own team, a Member (Člen týmu) uses its TVs. And the names now match the powers: a team admin's audit view covers their team instead of everyone, only a superadmin creates teams, and DevTools — which was never really restricted — is now openly available to every role, listing only the TVs you're allowed to see.
The amber marking now covers every superadmin-only block on the device detail — the management card and the emergency power card included, with the explaining legend under the last of them. And the emergency power restart is now truly superadmin-only: the server refuses anyone else, and nobody else sees the button.
On a device's Informace tab, everything only privileged roles can see — the IP addresses and the live power meter — is now tinted amber with a small edge stripe, and a one-line legend explains the colour. The live power card itself became superadmin-only, so regular users simply don't see it at all.
The keyboard button now glows green while it's driving the TV. The remote-layout switcher went back under the remote — with the layout jump fixed properly — so the volume row lines up with the top of the picture again. And the picture controls (straighten, quality) moved out of the tab row to sit right under the video, where they belong.
Click the live picture and your keyboard becomes the TV remote: arrows steer, Enter confirms, Backspace goes back, and R, G, B, Y press the colour keys. A small badge shows when the keyboard is live (Esc turns it off), and every keystroke visibly presses the matching button in the on-screen remote.
The remote pane got its walkthrough polish: launching apps is now a proper dropdown fed by what's actually installed on the TV, the text-and-apps drawer reads as one card with a clear name, the layout no longer jumps when switching remote layouts (the switcher moved on top), and the picture controls sit together neatly even on narrow screens.
A signing certificate covers a list of TVs, but the app never showed which — and regenerating it for a new TV started from a blank list, quietly dropping the old ones. Now the stored certificate card names every TV it covers, and "Replace" starts from that full list, so adding a TV really just adds it.
The app-signing wizard's first step asked you to run a helper script you had no way to obtain — running the command ended in "file not found". The helper now lives on the server and the command fetches it for you before running it. A stray dropdown arrow was also nudged into place.
The device form grew up: network addresses now sit in one clean grid — a labelled row per component, matching fields and buttons, technical MAC addresses in a technical font, and the camera-calibration status shows the exact date and time. App signing got two clear tabs (generate automatically or upload by hand) with a step-by-step guide, and the camera-type menu only offers cameras we actually own.
The device-health monitor moved out of everyone's way: it now lives in Settings as an admin tool. The one piece regular users care about stays in sight — an offline device now says right in the device list when it was last online.
Devices you've starred now sit at the top of the device list, whatever sorting you choose. Prefer the plain order? One tick in the sort menu turns the pinning off.
An open filter or sort menu on the device list now closes on a click anywhere else on the page — before, clicking the header or footer left it hanging open.
Sorting now lives in a single neat button — field and direction together in one menu. The filter checkboxes were redrawn to sit right in the light theme. And the little power readout on grid cards found its home: a small chip in the card's corner that stays put instead of floating over the neighbours.
Device-list filters now select more than one value at a time — several teams, brands or states at once, each filter a neat checkbox menu. Grid cards say what they mean: brand and model each get their own line. And coming back to the list from a device is instant — the app remembers what it just showed you instead of reloading everything from scratch.
The device list got its tools back. A well-meant rule had been hiding the search box, the list/grid switch, sorting, filters and the column chips until a list reached about eight items — with two real devices that meant no tools at all. The rule is gone: the toolbar now always shows, on devices, recordings and monitoring history alike.
The sign-in screen's logo comes alive: every half minute the stack of screens advances like a conveyor — a new edge appears from afar, the bars ride down, and the front one unfolds into the next TV, nudging the old one away. And the two things you couldn't change before signing in — light/dark theme and language (CZ ⇄ EN) — now sit as subtle controls in the footer.
The install checklist now tracks the actual work in real time — the TV box streams each step (connect, push, install, launch…) the moment it finishes and it appears on screen right away, instead of the whole list arriving at the end and the app faking the progression.
The install checklist now reveals each step one at a time as it finishes (rather than all at once), each step's full technical detail is available by clicking it open, and the working spinner is clearer.
More install-tab refinements from a live test: the signing step now visibly shows it's working, the step list is clean (no raw device logs — details show only when a step fails, and the "uninstall previous version" step is now named properly), and the drop area appears immediately while certificates are verified quietly in the background.
Small refinements to the new install tab after a live test: the drop area sits tidily under the tabs (no big gap), picking a package clearly shows the progress starting, the step checklist looks nicer, and a device that already has certificates goes straight to the drop area instead of briefly flashing a "no certificates" message.
Putting an app on a TV is now one step. The device's "Install" tab is a single drop area: drop an app package and it's automatically signed, sent to the TV, installed and launched, with a live checklist of each step. The old manual certificate + signing screens are gone from here — certificate setup moved into the device's edit form, where it's set up once.
Tidies up text that used to leak the app's internal wording into the Czech interface. The activity log now describes every action in plain language (saved a watch, signed an app, connected an app…) instead of showing a raw internal event name, and the monitoring screen shows its scheduler messages — like "the TV is in use, a live session wins" — in your chosen language.
Polish from a fresh whole-app UX/UI audit — a screen-by-screen designer's pass in light and dark. Batch A tightens consistency and brand: a redundant version stamp is gone from Settings, the auto-play preference matches its neighbours as an on/off segment, the device-health rows no longer say "Online" three times, recording key-counts show one consistent glyph + number, and the Teams password action reads as an action. Batch B adds a loading/shell layer: the top navigation bar is present the moment a screen opens (instead of appearing only after the data loads), and every section shows a loading state during that wait rather than a brief empty flash.
Brand polish on the app shell. The top navigation bar is now larger while you're scrolled to the top — showing the full screenwhere wordmark — and smoothly shrinks to the compact bar as you scroll down. The sign-in screen becomes a hero: a big stacked logo (mark over a two-line screen / where wordmark) with a soft green brand glow behind it, in both light and dark.
Follow-up to the relay fallback: on half-open networks the relay itself must be reached over TCP/TLS — a relay reached over UDP dies exactly like the direct path. The fallback now uses exclusively the TLS relay on port 443.
Some restrictive networks are half-open: connection checks pass but the video packets themselves are silently dropped, so the player looked connected while showing nothing. The player now detects that signature (connected twice in a row with zero media arriving) and re-dials exclusively through the TLS relay, which such networks do pass.
Live camera now reaches phones and laptops on restrictive networks — train, hotel and corporate wifi that block everything except HTTPS. The media path can relay over TLS on port 443 (indistinguishable from HTTPS to firewalls), and the web player now fetches its full relay configuration from the server before connecting. Built and TLS-verified live from a moving train on ČD's CDWIFI — the network that exposed the gap.
When the camera stream can't come up at all (some public/corporate networks block the media path), the terminal "video failed" message no longer keeps the spinner spinning as if still trying — and the message text is properly centered. Waiting states (connecting, TV offline) keep their spinner.
The v1.62.2 brand mark family is reverted (same-day owner call): at pill size the generic check / play / person / lock glyphs say what a state means at a glance, five near-identical screen marks didn't. Legibility wins over brand consistency for functional icons; the offline alert mark stays.
The set status pills (Volné / Ovládáte / Používá se / Nedostupné / Offline) now all carry the brand state-mark family — one "stack of screens" mark, the front screen's glyph tells the state, the pill's colour tells whose. Completes the state-icon rollout the offline badge started in v1.61.1.
A test-coverage batch (the suite grows to 53 files / 1468 assertions) — the Samsung pairing state machine now runs against a mock TV in CI, including the stale-token self-heal. It also surfaced one real fix: a pairing attempt the TV refuses no longer leaves a half-open socket behind.
The live identifiers deferred by the rename are migrated — nothing visible changes in the app. Filesystem paths on the site box and the cloud VPS now say screenwhere, the site agent runs as one templated screenwhere-agent@<set> service per set, and new installs pair with the TV as "Screenwhere Agent" (existing sets keep their stored pairing untouched).
The new logo family now shows up across the product: a two-tone wordmark (login, app footer, these docs), the brand alert state mark on offline badges, and the real mark + favicon on the docs site.
The internal working code name is retired — the product is now called Screenwhere (screen + anywhere). Every user-visible surface is renamed: the app wordmark and titles, the burned-in recording watermark, the sign-in pages, MCP metadata and these docs. The same release ships the final logo — the "stack of screens" mark — as the app icon, nav mark and first favicon.
When the TV dropped the remote-control connection (standby, or two agents sharing one TV), the box kept using the dead handle forever — keys silently went nowhere and the calibration card refused to open until the agent was restarted. A failed send now reconnects once and retries by itself.
The straighten-view renderer assumed every camera streams 16:9, so a 4:3 camera (like set-01's 5MP Reolink) had its straightened picture squeezed to 75 % width — calibration discs could never reach their target rings. The output now letterboxes to the TV panel's own 16:9 and the lens model uses the camera's real aspect; 16:9 cameras are unaffected.
The faster live detection could lock onto a frame before the TV browser had gone fullscreen, so its toolbar shifted the card down and the calibration came out offset. It now waits for fullscreen to settle (and re-asserts it during the search) before detecting.
The "finding the markers" step used to sit for several seconds fetching slow snapshots from the camera. It now uses the live video stream instead — you watch the camera feed the whole time, and the colour discs are found within a frame or two of the card appearing. Much faster, and it always looks like something is happening.
The calibration "capturing…" status used to flicker a few times before detecting, because every retry re-asserted fullscreen and waited over a second. Now only the first try does that; the rest just re-snap quickly until the colour discs appear.
The alignment targets in the rectified pane are now coloured circles with a centre dot — one per corner, matching the disc colours — instead of the old grey squares, and the draggable handles wear their corner's colour too. Each disc on the TV card also gained a precise white centre dot to aim the handle at.
The frame could be captured before the TV finished drawing the colour card, so detection ran on a blank screen. It now retries the capture until the card is up before placing the handles.
The calibration card now shows four big solid colour discs — red, green, blue, yellow, one per corner — instead of the old black squares that were hard to spot in a dim room. The app finds each by its colour, so the handles land on the markers automatically and it can't get the corners mixed up. Missing a colour (bad framing) just falls back to pre-placed handles you nudge.
The "live preview" toggle in the calibration editor used ~1 fps snapshots and looked choppy. It now dials a real video stream into the editor — one decoder, full frame rate, as smooth as the live detail — with the rectified result rendered from it every frame.
Marker detection is fragile in a dim room, so the four handles are now always pre-placed — from the previous calibration when there is one, otherwise on the aim guides — and you just nudge each onto its marker. And the live stream behind the calibration dialog now genuinely stops decoding (it was being revived by the camera self-heal loop), so dragging is smooth.
Slider ranges now match real edits (lens tweaks live within ±0.1), the marker detection pre-places the handles reliably — it looks near the previous calibration first and automatically re-captures when the TV was still loading the card — and the left pane can switch to a ~1 fps live preview. Buttons that did nothing useful are gone: the TV returns automatically on save or close.
Opening the calibration now does everything itself: the card loads on the TV with a narrated progress line, a frame is captured, the markers are found automatically and you land straight in the editor. The handle mapping was fixed — handles now belong exactly on the marker centres and the result is the whole panel edge-to-edge, with a saved zoom (both directions) for that sliver of margin around the screen. The background live stream pauses while calibrating, so dragging stays smooth.
The same display-only straightening the live view got is now available when reviewing recordings — a toggle next to the speed buttons, shown when the device has a saved calibration. Recordings stay raw on disk; the current calibration is applied at playback, so clips recorded before a camera re-aim may not line up perfectly — that's why it's a toggle.
The straightened view held only within one session — coming back showed the raw picture again. The server had it stored all along; the app's device-list loaders copied an explicit list of fields and the straightening wasn't on it, so every fresh page load dropped it. Now it carries through: a saved calibration applies on every visit until you recalibrate or remove it.
Saving the device-edit form right after calibrating brought the raw view back — the app's cached device list predated the calibration, so it "forgot" the fresh straightening until a reload. The calibration save now updates that cache too, so the straightened view sticks through any navigation.
"I saved — and now what?" The save now finishes the job: the TV returns to what it was showing, the live player's straighten toggle switches on for that device, and the dialog closes. Open the live detail and the stream is already straightened. Recordings stay raw by design.
The calibration dialog now leads with the live picture: aim the camera so the on-TV markers overlay the dashed outlines, press "Dokalibrovat" — and the four marker centres are detected automatically, dropping you straight into the straighten editor with the handles pre-placed. One save stores both the checks calibration and the view straightening. Clicking the centres by hand remains only as a fallback when detection isn't confident.
The first shipped editor looked nothing like the concept the owner approved. It is now the mockup, faithfully: original view with four draggable green handles on the left, the straightened result with ghost marker outlines, guide lines, zoom, grid and an A/B toggle on the right. Dragging is smooth too — frames upload to the GPU once, and repaints batch into single animation frames.
Opening the live screen made you the device's holder — and the calibration dialog then refused with "someone is using this device", even though that someone was you. Calibration now proceeds when the session holding the device is your own; a device held by another user still says no.
A camera never hangs perfectly — the picture is tilted and the wide lens bows straight lines. The view can now be straightened in software: in the calibration dialog you drag the four marked card centres, dial the lens curvature until the markers settle into their ghost outlines, and save. The live player then offers a "Straighten picture" toggle that re-projects the stream on the GPU — no extra decoding, no added latency. Purely cosmetic: recordings stay raw and the automated checks keep their own calibration, so this can never break a watch.
POST /app/set-view-calib, audited); the on-TV card gained thin grid lines to dial the lens against.A pocketed phone kept holding a TV — and kept the recording signal on — for 7.5 hours overnight, because a background tab's connection never dies on its own. The relay is now the authority: a session that shows no sign of life for 15 minutes gets its control auto-released and its watching stopped (which also ends the recording). While you're actually there — tapping, typing, or just watching the live picture with the screen on — the app quietly pings the relay, so a real session is never cut. Coming back after an idle stop resumes with a single tap.
IDLE_RELEASE_MS, 15 min default, audited); a lightweight activity ping (≤1/min, only while visible and interacting-or-decoding); a running scenario is never reaped; camera stage says "paused due to inactivity — tap to resume".Owner report from the phone: the alert-webhook URL field and the access-token name field looked alien — grey browser-default boxes with a light border. The app styled inputs per screen and had no global base, so a field outside any styled scope fell back to the browser's dark-mode default rendering. There's now a zero-specificity base style for all text inputs (surface background, themed border and focus ring, placeholder colour), so a bare input can never render browser-default again — and every screen-specific style still wins.
Two long-deferred UX leftovers closed in one small batch. Every active ★ in the app — favourite devices on the dashboard and in the picker, permanently-kept recordings in Záznamy and on dashboard poster badges — now turns the same gold (previously half of them turned the brand green). And the standalone add/edit-device form finally got its dedicated walkthrough.
--star colour token (slightly darker gold on light surfaces for readability); applied to favourite buttons, keep buttons and poster badges.Real-phone report: after camera fullscreen, the remote seemed dead. The full chain check (relay + Pi agent logs + a camera snap) proved every key press DID reach the TV — its menu cursor moved exactly as pressed. What actually broke was the picture: the fullscreen→remote transition made the phone throttle the video decoder mid-session, and since v1.48.3 the app (correctly) no longer tears down a connection whose data still flows — so the preview showed the last decoded frame, frozen, and the TV's reactions were invisible.
engine.kickStream(), rate-limited to once per 15 s). Hidden previews stay untouched — that throttling is fine and saves battery.Owner pick after comparing variants on a real phone: the little camera preview on the remote tab no longer sits in the page flow — it floats over the remote. By default it tucks into the top-right corner under the sticky nav, and it stays put while you scroll a long remote, so the TV stays visible next to the bottom rows. Don't like the corner? Drag the box anywhere — it snaps to the nearest corner on release, and each set remembers its corner.
position:fixed + a pointer-drag handler with snap-to-nearest-corner; the corner is persisted per set (tv-pip-corner:<set> in localStorage). ✕ / re-show chip / ⤢ jump-to-camera and the v1.48.4 real-element reparenting are unchanged; desktop ≥ 740 px untouched.Follow-up to v1.48.3: the little camera picture stopped blinking but stayed black. A second video element mirroring the same stream can't render on phones — the decoder is throttled while the only rendered sink is missing, and a sink attached mid-stream doesn't wake it up. So the PiP now borrows the real camera video element: it moves into the PiP box on the remote tab and moves back when you leave. One element, always visible somewhere — the picture never stops.
pipSync reparents #cam-video instead of mirroring the stream; DOM identity survives the move, so stream, stats and fullscreen keep working. Desktop is explicitly excluded via the same media query that hides the PiP, and the separate PiP video element is gone from the page.On a real phone, the little camera picture on the remote tab kept flashing up and vanishing in an endless loop. The culprit was our own 7-second liveness watchdog: it only trusted decoded frames as proof of life, and mobile Chrome pauses decoding while the video element is hidden — so a perfectly healthy stream was torn down and rebuilt every ~7–8 seconds. Receiving data now counts as alive too.
framesReceived counts alongside framesDecoded in the engine's stream-liveness check, and the PiP video gets an explicit play() nudge when the stream attaches (mobile autoplay).Owner preference after the first real-phone session: tapping the burger now opens a classic dropdown menu anchored right under the header — where the eye goes after tapping — instead of a sheet sliding up from the bottom edge. Everything else is unchanged: it still closes when you pick a section or tap outside.
Two fixes straight from a real phone. Visiting …/app without the trailing slash used to render the app as raw unstyled HTML — the browser resolved every asset URL against the site root. /app and /docs now 308-redirect to the slashed form, query string preserved. And opening the app with a stale stored session now lands on the login screen instead of an empty dashboard with zero devices and no user.
The Monitoring run history no longer stops at the newest 100 runs. A "Load older" button under the list fetches earlier runs 50 at a time, all the way back to the start of the retained ring. Pages are keyed by run id — not by offset — so rows don't shift when new runs land between clicks, and the verdict chips keep filtering everything that's loaded.
GET /app/scenario-runs gains a before=<runId> cursor and a more flag; it composes with scenarioId and limit, and a pruned cursor simply returns an empty page (the ring prunes oldest-first, so nothing older survives either).A new perf: condition reads the running app's real performance numbers over the same debug channel el: uses — no camera involved. memoryMB asserts the live JS heap ("the app stays under 150 MB"), domReadyMs and loadMs assert how fast the page actually got ready. Works in check/waitUntil/if/repeat.until, so a watched scenario can fail the moment an app starts leaking or loading slowly.
check: { perf: memoryMB, max: 150 } · waitUntil: { perf: loadMs, max: 8000, timeout: 15000 } — editor autocomplete, validation, the VS Code schema and the inspector's "Otestovat teď" all know the new kind.The mobile pass is complete. On a phone the remote tab now docks a small live camera preview above the volume row — dismiss it with ✕ (per device; a dashed chip brings it back) or tap ⤢ to jump to the full camera. It mirrors the already-running stream, so it costs no extra bandwidth. Plus phone polish: a one-line sticky header on the device detail, two-line title clamps, settings controls that drop under their label, and a stacked install form.
First build wave of the mobile/tablet pass. On phones the dashboard stats become a swipeable row with position dots (the 4th card used to be clipped and unreachable, the storage card collapsed into a jumble), device-list titles no longer paint under the status chip, the nav sheet closes after picking a section, and the review player stops scrolling sideways. Tablets get one deliberate threshold: at 740 px the camera-beside-remote layout and the horizontal nav flip together, so iPad mini portrait now gets the full arrangement.
Found during the mobile/tablet audit but broken at every screen size since v1.32: the run-history trigger glyphs (plán/ručně/kamera) shipped without a size rule, so the icons rendered ~135 px wide and every history row took half a screen. One CSS rule returns them to inline text size.
An el: condition now reads the player itself: playing ("the video element truly renders — not paused, not ended, buffered enough") and minTime (currentTime at least N seconds) prove playback from inside the instrumented app, no camera needed. Separately, every install now uninstalls an already-present same-name package first, so installs always start clean.
waitUntil: { el: "video", playing: true, minTime: 5 } — usable in check/waitUntil/if/repeat.until; editor autocomplete, validation and the VS Code schema know the new fields.Found while verifying the add-debug chain live: the DevTools pane fetched the target list without the auth header every other call sends, so it always got an empty list and showed "no debug module" even with a target registered. Now it authenticates like the rest.
Two bugs surfaced on the first live end-to-end run of "add debug to this app" on a real TV. The injected debug client now registers under a title the DevTools tab (and el:/picker) can actually select, and installing over an app that's already on the TV under a different certificate no longer dead-ends.
window.ChiiTitle to the set's id (chii ignores the old data-name guess), so the target is selectable and el:/picker can reach it.No more hand-writing CSS selectors for el: conditions: the editor's new 🎯 Element button lists the running app's elements live — selector, label, focus state — and one click drops a "press DOWN until it has focus" block into the scenario.
Scenarios can now assert what the running TV app's DOM shows — "press DOWN until the row named Movies has focus" — through the debug plane, no camera involved. A structured, read-only probe runs inside the instrumented app; the run log reads out what the app actually showed.
Authored scenarios sync between the relay and local YAML files: pull them into a repo, edit them in VS Code (each file carries a $schema header, so autocomplete and validation just work), push them back — authenticated by a personal access token with the same author/admin rules as the app.
A scenario you just wrote can go under monitoring without hunting for it in another screen: the editor toolbar gains a ⏰ Watch button (admins) that saves your edits and opens Monitoring's new-watch dialog with the scenario already picked.
The TV browser drops out of fullscreen after ~15–20 s, so the calibration card's geometry could differ between captures. Capture and save now re-assert fullscreen (one OK press) and use a fresh frame — the marked geometry and the stored camera reference are always the fullscreen card.
The calibration's two consumers land — the fallen-camera incident's fix is complete. Every check frame on a calibrated set is warped into a canonical panel space (regions and references live in TV coordinates and survive a camera re-aim), and every scheduled run first verifies the camera still sees the TV where calibration left it.
Polish from the first real remote calibration (a set aimed from 1000 km away): the device-edit forms now re-fetch the "Calibrated" status the moment the calibration overlay closes, instead of showing the stale value until a reload.
The fallen-camera incident's systematic fix begins: a guided calibration flow in the device-edit forms. The TV shows a full-screen test card (opened in its browser — no app install needed), the installer aims the camera against ghost marker outlines over a live view, clicks the four marker centers and saves.
Tags in the scenario library used to be decoration — the search box quietly matched them, but nothing told you. Now clicking a tag chip filters the list (clicking it again clears), chips respond to hover and keyboard, and the search bar stays visible whenever the library has anything in it.
Scenario checks stop being guesswork: open the inspector on any condition, see the live camera frame with the checked region outlined, drag the strictness knobs and hit "Test now" — PASS or FAIL with the measured numbers, before the scenario ever runs.
The remote's occasional tools (type text, launch an app) fold into one "Akce" block; expert tabs show only to the roles that use them, and the install/signing workflow finally gets the full screen width. Settings regroup into four titled sections.
Scenarios from recordings and scenarios written in the editor now live in one list — each row wears a small badge saying where it came from, and a recorded one converts to a fully editable scenario in one click.
The app's sections — Zařízení, Scénáře, Hlídání, Záznamy — now live in one persistent top bar with icon tabs, an active indicator and the familiar avatar, on every screen. No more bouncing off the dashboard to get anywhere.
A whole-app UX audit ran this week; this release ships its first batch: schedule times in your timezone, list toolbars that appear only when a list is big enough to need them, one time format, one version number, SVG icons everywhere — and the alert webhook is now set in Settings instead of on the server.
Scenarios can now assert against a stored reference frame, not just OCR text: capture what the screen should look like, and every run compares the live camera against it — content and position. A layout that slid 20 pixels down fails the check even when every word still matches.
The launch row and AI agents now ask the TV for its installed apps instead of you hand-typing Tizen ids: open the row, get a type-ahead list of names (Netflix, YouTube, Disney+…), pick, launch. Agents get the matching list_apps tool.
The sibling of v1.28's text entry: start any installed app by its Tizen id in one step — a "Launch app" row on the live remote, and a matching launch_app MCP tool for AI agents. No more HOME-menu navigation to get an app on screen.
No more arrow-key hunt-and-peck on on-screen keyboards. The live remote gains a text-entry row that types straight into whatever input field is focused on the TV, and AI agents get the matching send_text MCP tool — search boxes and login forms filled in one step.
SendInputString) shipped in v1.23 and is hardware-verified.Scheduled scenarios now watch your apps, and a monitoring screen shows what happened: a card per watch with the last verdict and a colour-coded chip per TV, a filterable run history, and — for a failure — the camera frame from the exact moment a check failed. A watch can target several TVs or a whole team, running them in parallel or one at a time.
Every scenario run is now recorded: each step with its timing, every check with its measured match, and the final verdict. Scenarios can also run unattended on a schedule — nightly at three, or every half hour — and if a scheduled run doesn't pass, an alert goes out to your webhook. A scheduled run never interrupts a person: if someone is using the TV, it steps aside and records that it did.
Until now a scenario replay was blind: it pressed the keys and a human watched the camera to judge the result. Scenarios can now assert what's actually on the TV — "the screen says Welcome" — through the real camera, with no changes to the app being tested. A run finally ends with a verdict: PASS, or FAIL with what the screen actually said.
Authored scenarios got their editor: a full code editor with syntax highlighting and autocomplete, opened straight from the Scenarios screen. One scenario, three interchangeable notations — YAML, JSONC and TypeScript — switch tabs and nothing is lost. Below the code sits a debug-style run console: pick a TV, run the scenario, and watch each step light up in the code with the exact wall-clock time it fired.
Until now a scenario was whatever you happened to record. It can now be an authored document: a readable list of steps — press these keys, wait, type text, launch an app, repeat a block — that is stored on the relay, validated against a published format, and run step by step onto any TV. Recordings convert into editable documents with one call, and the format already reserves room for on-screen checks (the upcoming camera verdicts).
Have the same TV's live view open in three tabs and on your phone? The app now treats all of them as you: any window can drive the TV, the banner shows "you're using this in 4 windows", and Release means release — it frees the set from every window and session at once.
A downloaded QA clip no longer labels presses with plain text — it draws the remote's buttons. The last few presses ride along the bottom of the picture as icon chips: the newest is big and glows in its category colour with an English label, older ones shrink and fade to the left, so a reviewer sees the sequence context, not just the last press.
Saved (★) recordings with key presses are now a first-class scenario library, and a scenario can replay on several TVs in parallel. The new Scénáře screen turns timestamps into readable test scripts — name them, tag them, search them — and runs one on any set of free TVs with a live progress row per TV.
AI clients can now connect with a click-to-connect login instead of pasting a token. Screenwhere is its own authorization server — you sign in with your existing local account (email, password, 2FA) and the connected app acts as you. OAuth is a front door onto your account, not a separate identity or a third-party login.
Builds on the read-only MCP server: an AI agent can now control a TV through MCP — press remote keys, switch power, and replay recorded scenarios — each action as the user who drives it, with the relay enforcing who may control and auditing every action.
Groundwork for letting an AI agent drive your TVs: mint a personal access token and point an MCP client at your sets to read state, health, recordings and stats — each agent acting as the user who drives it (pass-through identity), never more.
The DevTools tab can now turn a customer's app into a debuggable one. If the running app has no debug client, it offers to add one — the box injects our debug client into the app's start page, re-signs the package with the site's certificate, and reinstalls it on the TV, then the DevTools attach as normal. The injected client self-activates on every boot, so it's a one-time step.
A smart plug on weak WiFi can stall its first connection for seconds; the agent's flat 4-second timeout killed exactly those requests, so energy polling failed and the log filled with an identical line every 20 seconds. Plug calls now use a generous timeout plus one retry, and failure logging is rate-limited.
The live-power W callout and gauge on the Dashboard cards and device grid were tuned for dark mode — in light mode the callout was a dark blob and the gauge's level dot vanished on the white card. Both now adapt to the theme; dark mode is unchanged. Purely visual.
Each recording in the Dashboard's per-device "Záznamy" strip now shows how many buttons were pressed during it — a small keyboard glyph + count on the right of each row, matching the recordings grid. The number was already captured; this surfaces it on the Dashboard too.
Screenwhere now mints the Samsung author + distributor developer certificates itself, straight from Samsung's certificate authority — the last manual step in getting an app onto a TV. The only thing left for a person is a one-time Samsung account sign-in. Proven end-to-end on the real office TV.
sdb), and click Generate — the certs are created and stored encrypted on the box, ready for the existing sign + install flows..p12 with the vendored Samsung CA chain. Private keys never leave the box.localhost, so a thin operator-side helper (tools/samsung-token-helper.py) captures the token and hands it over through a code-gated, single-use relay endpoint; the token is never persisted or logged.The package we push to the TV to install it now reuses one stable filename that each install overwrites, so it never accumulates — instead of leaving a new file behind every time.
sdb shell is locked to app-management commands (no remote file delete), so a timed cleanup isn't possible; overwrite-reuse keeps the footprint to a single small file.Our signature was cryptographically valid but every Samsung TV rejected it. Fixed — and verified on the real office TV, including re-signing another company's app with our own certificate so it installs.
The set detail's "Instalace" tab now installs a signed app onto the Samsung TV over the box — no operator laptop in the loop — instead of only signing it for a manual install.
.wgt to the TV over the vendored sdb, installs it, and optionally launches it, reporting each step (connect → push → install → launch).Aligned the signer to what current Tizen Studio emits — SHA-512 digests, RSA-SHA512, exclusive canonicalisation — validated against two real Studio-signed reference packages.
A new app-sign plane: sign a Tizen install package with the site's own author + distributor certificates so a TV accepts it. Set detail → "Instalace" tab, super-only.
The live-power watt reading on the Dashboard device cards now sits as a callout to the left of the gauge, matching the device grid.
The device list and Dashboard device rows get a larger TV thumbnail, tinted by status.
The device cards' power indicator is redesigned from a mini sparkline into a glowing left-edge gauge driven by live wattage.
Device cards on the Dashboard and the "all devices" picker show a mini power trend from the smart plug's live draw.
Screenwhere now tracks the smart plugs' energy itself — all local, nothing goes to the cloud — with a super-only consumption/cost panel and an on-demand live-power sparkline on the set detail.
The "Restartovat napájení" feature — a hard power-cycle of the TV's wall socket via a Shelly Plug M Gen3 — verified on real hardware, with SHA-256 HTTP Digest auth.
The set-detail Záznamy tab reads cleaner: your own recordings no longer repeat your name on every row, zero-second durations are trimmed, and the Přehrát / Scénář actions got proper icons with a unified hover.
10 min 0 s → 10 min (the trailing 0 s is dropped on a whole minute).The device list and the recordings overview now show themed shimmer placeholders while they load, matching the Dashboard — so opening them reads as "loading" instead of looking frozen.
Turn recording on/off per session on the live screen (keep or delete what was captured), and a Dashboard that loads with a skeleton, never flashes the device list, and re-opens instantly.
The review reaches the live detail's recordings tab — it loads reliably, plays through the Review player, navigates consistently — plus a real live-camera fix.
127.0.0.1 as its WebRTC ICE candidate → media never reached the browser; bound the WebRTC UDP socket to the public IP. Clearer "network may be blocking the stream" failure message.The screen-by-screen review reaches the live set detail — a responsive remote, plane-editor fixes and a perf win.
The review reaches the device picker — lots of affordances, plus standalone device editing.
presence/release).The review reaches the Dashboard — lots of polish, plus two real bugs.
$q helper recursed and crashed every set detail since 1.3.0; the recording "3274 min" duration bug fixed; ★ double-toggle fixed.Finish the type-checking pass on the browser code — and the type checker earned its keep.
web/js modules now @ts-check-green (CI-guarded).curSet) — all from a shadowed i18n t().Break the 1.9k-LOC relay.mjs into focused modules — same behavior, now safe to evolve.
relay.mjs 1874 → 92 lines: a thin bootstrap wiring 13 focused modules; shared state mutated in place (no reference rewrites)..mjs files — scp them together.Prove it works, harden the public relay, and stop the bug class at the source — no rewrite.
tsc --checkJs types.Everything that was built is now written down — and versioned.
Manual MAC entry, burned-in QA video, a live bandwidth gauge, safer installs.
A glanceable home with favourites, recent recordings and usage charts.
The whole app speaks Czech and English, switchable live with no reload.
Record a session, replay the exact key-presses, keep storage in check.
A trustworthy log, offline alerts, a remote power-cycle, and a camera that only streams when watched.
Real, tamper-proof logins with two-factor and trusted devices.
Find devices on the network, follow them by MAC, one-button TV power.
Add and edit devices from the app, with the box configured from the cloud.
Shared access with teams, real accounts, graceful hand-over.
The app gets its real look and is wired to the live system.
Run many TVs from one app and one box per site.
Control a TV on a foreign network, with live video and debugging, from anywhere.