| Time | Topic | Source | Payload |
|---|---|---|---|
| No events yet | |||
| {formatTime(ev.received_at)} | {ev.topic} | {ev.source_type} | |
| Name | Type | Streams | Status |
|---|---|---|---|
| No cameras configured | |||
| {cam.name} | {cam.type.toUpperCase()} | {String(props.streamCounts.get(cam.id) ?? 0)} | {cam.enabled ? Enabled : Disabled } |
Connect to an ONVIF camera or NVR by host and credentials. Profiles from the same video source are imported as streams on one camera.
Video sources reported by {props.host}. Each source imports as one camera with its profiles saved as streams.
| Profile | Encoding | Resolution | FPS | Stream URI | |
|---|---|---|---|---|---|
| No profiles returned | |||||
| {p.profile_name} | {p.encoding ? {p.encoding} : "—"} | {p.width && p.height ? `${String(p.width)}x${String(p.height)}` : "—"} | {p.framerate != null ? String(p.framerate) : "—"} | {p.stream_uri} | |
Video sources reported by {props.host}. Each source imports as one camera with its profiles saved as streams.
{props.cameras.length === 0 ? (| Role | Profile | Encoding | Resolution | FPS | Stream URI |
|---|---|---|---|---|---|
| {p.role} | {p.profile_name} | {p.encoding ? {p.encoding} : "-"} | {p.width && p.height ? `${String(p.width)}x${String(p.height)}` : "-"} | {p.framerate != null ? String(p.framerate) : "-"} | {p.stream_uri} |
Entities are reusable content blocks (a camera reference, an HTML snippet, or a web page). Bind one entity to any number of layout cells — edit the entity once and every cell updates.
| Name | Type | Detail |
|---|---|---|
| No entities yet | ||
| {e.name} | {entityBadge(e.type)} | {entityDetail(e)} |
| Name | Hardware | Last Seen | Status |
|---|---|---|---|
| No kiosks paired | |||
| {k.name} | {k.hardware_model ?? "—"} | {k.last_seen_at ? formatTime(k.last_seen_at) : "Never"} | {k.enabled ? Active : Disabled } |
{pc.code}
{formatTime(pc.expires_at)}
Enabled {" "}TOTP is active on this account.
Protect your account with a TOTP authenticator app.
Scan this with your authenticator app (Google Authenticator, Authy, etc.).
{props.secret}
Save these codes somewhere safe. They will not be shown again.
{props.description}
| Name | Details |
|---|---|
| None configured yet | |
| {item.name} {item.badge && ( {item.badge} )} | {item.detail ?? ""} |
No labels attached
)}| Role | Name | URI |
|---|---|---|
| {s.role} | {s.name} | {s.rtsp_uri} |
No streams configured
)}| Name | Resolution | Index |
|---|---|---|
| {d.name} | {String(d.width_px)}x{String(d.height_px)} | {String(d.index)} |
No displays associated with this kiosk
)}No labels attached
)}| Name | Color | Actions |
|---|---|---|
| No labels | ||
| {l.name} | {l.color ? {l.color} : "—"} | |
Layouts are standalone — they define a grid of regions and bind cameras or other content into them. Attach a layout to one or more displays from the display's edit page.
| Name | Displays | Priority |
|---|---|---|
| No layouts created yet | ||
| {l.name} | {count === 0 ? unattached : {String(count)} display{count !== 1 ? "s" : ""}} | {l.priority === "hot" ? hot : l.priority === "cold" ? cold : normal } |
Create an empty layout. You'll add cells visually on the next page, then attach the layout to one or more displays.
Hover a cell for + (add neighbour), resize handles (+W -W +H -H) and × (delete). Click a cell to edit content in-place.
Pick which layouts this display can show. The kiosk receives only attached layouts in its bundle.
{props.attachedLayouts.length === 0 ? (No layouts attached yet.
) : (| Name | Priority | Default | |
|---|---|---|---|
| {l.name} | {l.priority} | {d.default_layout_id === l.id ? Yes : ""} |
{props.attachedLayouts.length === 0 ? No layouts exist yet. Create one. : "All existing layouts are already attached."}
)}Physical HDMI displays. Created automatically when kiosks are paired.
| Name | Details |
|---|---|
| None configured yet | |
| {d.name} | {String(d.width_px)}x{String(d.height_px)} — index {String(d.index)} |