Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
33 lines
1.8 KiB
TypeScript
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>
|
|
)
|
|
}
|