Files
hamlab/web/src/Embed.tsx
T

33 lines
1.8 KiB
TypeScript

// One widget, alone, for an iframe in someone else's dashboard (Glance, Home Assistant…):
// /embed/widget?type=gauge&sensor=cpu.usage&token=<read-only token>
// &machine=grid &title=… &theme=dark|light|cyberpunk|retro &every=<ms> &bare=1
// It stays live over the sensor stream. The token must be a read-only sensor token; a
// full-control token is refused in a URL by design.
import { useSensorStream, Source } from './sensors'
import { Widget, WidgetData, WidgetSpec, WidgetType, WIDGET_TYPES } from './widgets'
import { applyTheme } from './themes'
const p = new URLSearchParams(location.search)
if (p.get('theme')) applyTheme(p.get('theme')!)
if (p.get('bare') === '1') document.body.style.background = 'transparent' // sit on the host dashboard's own card
export default function Embed() {
const sensor = p.get('sensor') ?? ''
const asked = p.get('type') as WidgetType | null
const type: WidgetType = /[*?[]/.test(sensor) ? 'list' : asked && WIDGET_TYPES.some((t) => t.id === asked && !t.many) ? asked : 'stat'
const spec: WidgetSpec = { id: 'w', type, sensor, title: p.get('title') ?? undefined }
const src: Source = { machine: p.get('machine') || null, token: p.get('token') ?? undefined }
const every = Number(p.get('every')) || undefined
const live = useSensorStream<{ w: WidgetData }>(sensor ? { w: sensor } : null, { src, everyMs: every })
return (
<div className={'embed' + (p.get('bare') === '1' ? ' bare' : '')}>
{!sensor ? <div className="error">embed needs ?sensor=</div> : (
<div className={'tile w-' + type}>
<Widget spec={spec} data={live.data ? live.data.w : undefined} src={src} />
{live.error && !live.data && <div className="sub">{src.token ? live.error : 'no token in the URL (&token=…)'}</div>}
</div>
)}
</div>
)
}