import { useState } from 'react'
import { api } from '../api.js'
import { usePolled } from '../hooks.js'
import Shot from './Shot.jsx'
import CameraLive from './CameraLive.jsx'
import { ago, Loading, Problem } from './Sites.jsx'
import CameraSetup from './CameraSetup.jsx'
// Cameras, onboarded from head office.
//
// The shop PC is still the thing that CONNECTS — it is the only machine on the
// camera's network — so everything here is desired state that its agent pulls
// and applies. That is why a new camera reads "waiting for the shop PC" rather
// than "connected": saying it is set up would be claiming something nobody has
// verified yet.
// verification is the second claim a camera card makes, and the one that
// matters: connected says the stream opens, verified says a person walking past
// can actually be recognised. Office1 was the first for weeks without ever
// being the second.
function verification(cam) {
const c = cam.check || {}
if (c.state === 'requested' || c.state === 'running') {
return { tone: 'idle', mark: '…', words: 'Checking now' }
}
if (c.state !== 'done') {
return { tone: 'idle', mark: '?', words: 'Not checked yet' }
}
if (c.kind === 'placement') {
return c.ok
? { tone: 'ok', mark: '✓', words: 'Recognises faces here' }
: { tone: 'bad', mark: '✕', words: c.headline || 'Cannot recognise faces here' }
}
return c.ok
? { tone: 'warn', mark: '!', words: 'Stream works — faces not checked yet' }
: { tone: 'bad', mark: '✕', words: c.headline || 'Could not reach the camera' }
}
export default function Cameras({ user }) {
const { data: cams, error, loading, reload } =
usePolled(() => api.cameras(), 20000, [])
const { data: sites } = usePolled(() => api.sites(), 0, [])
const [editing, setEditing] = useState(null)
// Only one camera streams at a time, on purpose. Every open view makes a
// shop PC upload, so a grid that went live all at once would put an estate's
// worth of cameras on the wire because somebody opened a page.
const [watching, setWatching] = useState(null)
const canEdit = ['admin', 'owner', 'manager'].includes(user.role)
const list = cams || []
// Counted off `state`, the one field the server computes, never off
// `connected`. Two places deciding the same fact is how a shop came out
// labelled Working, in green, above "2 of 3 cameras not connecting".
const up = list.filter(c => c.state === 'connected').length
const down = list.filter(c => c.state === 'not_connecting').length
const waiting = list.filter(c => c.state === 'waiting').length
const stale = list.filter(c => c.state === 'stale').length
return (
<>
{list.length} {list.length === 1 ? 'camera' : 'cameras'}
{up > 0 && <> · {up} connected>}
{stale > 0 && <> · {stale} not reporting>}
{down > 0 && <> · {down} down>}
{waiting > 0 && <> · {waiting} waiting for the shop PC>}
Cameras
Add one here and the shop’s PC will pick it up within a couple of minutes. Cameras already set up on a shop PC appear here on their own.