Skip to content

Updated

View MarkdownOpen in ChatGPTOpen in Claude

API Reference

Every public export of @glasshome/widget-sdk, with signatures, by entry point. The Widget SDK guide walks the important APIs with worked examples. Anything not listed here is internal and may change without notice.

Entry points

Import Use it for
@glasshome/widget-sdk Everything you use at runtime: defineWidget, components, hooks, the data layer, utilities, theming.
@glasshome/widget-sdk/schemas Zod schemas for validating manifests and publish payloads. Used by the CLI and Hub.
@glasshome/widget-sdk/vite Build plugins and helpers for vite.config.ts. The scaffold wires these for you.
@glasshome/widget-sdk/tailwind-sources A CSS file the build feeds to Tailwind. Referenced by the scaffold; not imported in widget code.

Most types named below are exported too. SolidJS conventions apply: Accessor<T> is () => T, and an argument typed Accessor<X> | X accepts a plain value or an accessor.

Always import the data layer from the SDK

Home Assistant hooks (useEntity, useService, …) are re-exported from the SDK on purpose. Importing them from @glasshome/sync-layer directly fails the build and would bundle a second, disconnected store. See Capabilities.


@glasshome/widget-sdk (main entry)

Core

function defineWidget<C = Record<string, unknown>>(
  definition: WidgetDefinition<C>
): WidgetDefinition<C>

Declares a widget. When definition.configSchema is present, it auto-populates manifest.schema (JSON Schema) and manifest.defaultConfig. See the guide.

const SDK_VERSION: string  // the installed package version, e.g. "1.2.0"

The host compares SDK_VERSION against each widget’s manifest.sdkVersion range.

Components

Widget carries its slots as static members (Widget.Icon, Widget.Title, …); each is also exported standalone.

Export Props (key) What it is
Widget variant, tone, color, colorTo, gradient, loading, emptyState, gestures, class The container: shell, gestures, theming. See Widget component.
WidgetContent / Widget.Content children, class? Layout wrapper.
WidgetIcon / Widget.Icon icon, color?, dimmed?, entityCount? Entity icon tile with adaptive color.
WidgetTitle / Widget.Title children Primary label.
WidgetStatus / Widget.Status children State text.
WidgetValue / Widget.Value children Numeric/formatted value.
WidgetSliderFill / Widget.SliderFill value (0–100), color?, isDragging? Animated background fill for slider widgets.
WidgetDialog see WidgetDialog component Settings/detail dialog. Renders no chrome of its own; inject UI via props.

Widget hooks

function useWidgetContext(): ReactiveWidgetContext

Reactive widget context. Must be called inside the <Widget> tree.

interface ReactiveWidgetContext {
  updateConfig: (config: Record<string, unknown>) => void;
  dimensions: () => { width: number; height: number };  // deprecated since 1.9.0, use useWidgetDimensions()
  registerDialogOpener?: (open: ((tab?: string) => void) | null) => void;
  callService?: ServiceCallFn;  // present when capability-routed by the host
}

isEditMode was removed in 1.9.0 (nothing read it; the host provided a constant false).

function useWidgetDimensions(): () => { width: number; height: number }

Measured shell size in CSS px. Call it in a component inside <Widget>; it throws anywhere else.

ctx.dimensions() is deprecated (removed in 2.0.0): outside <Widget> it silently read the host’s stub, which returns (0,0) forever, locking a widget into its smallest layout. This hook throws instead.

function useWidgetEntityGroup<TData = unknown>(
  options: UseWidgetEntityGroupOptions<TData>
): UseWidgetEntityGroupResult<TData>

Aggregate one or many entities with presets + empty state. Options: reactive entities: Accessor<EntityView[]>, an aggregationMode ("light" \| "sensor" \| "switch" \| "binary-sensor" \| "none") or a custom calculateGroupData, plus emptyStateConfig, sensorGroupType, allEntitiesMode, and minEntities. Returns entities, groupData, aggregatedData, emptyState, hasEntities, and count accessors. Full tables in the guide.

function useWidgetDialog(defaultTab = "controls"): WidgetDialogReturn

Open/close + active-tab state for WidgetDialog. Returns showDialog, setShowDialog, openDialog, closeDialog, activeTab, setActiveTab, and a dialogProps bag to spread onto <WidgetDialog>.

function useWidgetGestures(
  config: () => GestureConfig,
  orientation?: () => "horizontal" | "vertical" | "square"
): GestureHandlers

Tap, hold, and slide gestures for any pointer. GestureConfig = { tap?, hold?: { action, delay? }, slide?: { value, onChange, min?, max?, orientation?, activationDelay? } }. Pass the returned GestureHandlers to <Widget gestures={…} />; call .dispose() in onCleanup.

function useReducedMotion(): Accessor<boolean>      // prefers-reduced-motion; false if unavailable (SSR-safe)
function useIntersectionPause(el: Accessor<Element | undefined>): Accessor<boolean>  // true while off-screen
Export Kind What it is
WidgetCtx SolidJS context The context object behind useWidgetContext. Advanced; prefer the hook.

Config schema helpers

Since 1.4.0 the config surface is defineConfig + field.* (see the Config API):

function defineConfig<S>(shape: S): ConfigSchema<S>;
const field: {
  title(): Field<string | undefined>;
  text(o): Field<string>;
  number(o): Field<number>;
  toggle(o): Field<boolean>;
  choice<T>(values: readonly T[], o): Field<T>;
  entities(domain: string, o?): Field<string[]>;
  entity(domain: string, o?): Field<string[]>;
  area(o?): Field<string | undefined>;
  stringList(o): Field<string[]>;
  group<S>(shape: S, o): Field<Infer<S>>;
};
type Infer<C>;   // config type from a schema, without naming zod

z is re-exported from the SDK as a permanent escape hatch for schemas field.* can’t express.

// Deprecated — use field.* / defineConfig (removed in a future major).
const widgetFields: {
  title(): ZodType;                                              // → field.title()
  entityIds(domain: string, opts?: { deviceClass?: string }): ZodType;     // → field.entities(domain, opts)
  singleEntity(domain: string, opts?: { deviceClass?: string }): ZodType;  // → field.entity(domain, opts)
  areaId(): ZodType;                                            // → field.area()
}

widgetFields and raw z.object config are deprecated. Run bun widget migrate config. See the Config API.

Home Assistant data hooks

Reactive accessors over the host’s live store. Each entityId arg accepts a string or an accessor. Declare every domain you read in your capabilities: that declaration is what the user sees and approves when they install your widget.

Export Signature What it is
useEntity (id: Accessor<string> | string) => Accessor<EntityView | undefined> One entity by id.
useEntities (ids: Accessor<string[]>) => Accessor<EntityView[]> Many entities from an id-array accessor.
useArea (id: Accessor<string> | string) => Accessor<AreaView | undefined> An area and its entities.
useEntityHistory (id) => Accessor<EntityHistoryData | undefined> Recent state history.
useEntityStatistics (id, options) => Resource<StatisticValue[]> Long-term statistics; resource exposes .loading / .error.
useForecast (id) => Accessor<WeatherForecastsData | undefined> Weather forecast for a weather entity.
useCamera (id) => { stream: Accessor<CameraStreamData | null>; refresh: () => void } Reactive camera stream.
useCalendarEvents (id, options?: { daysBehind?: number; daysAhead?: number }) => Accessor<CalendarEventsData | undefined> Live events for a calendar entity, pushed by Home Assistant. Window defaults to 7 days behind and 35 ahead. Data carries events, loading, error and the subscribed start/end. Since SDK 1.10.0.
useStore <T>(selector: (s) => T) => Accessor<T> Direct store selector. Escape hatch.
byDomain Accessor<Record<string, string[]>> All entity ids grouped by domain.

Connection & locale hooks

Export Signature What it is
useConnection () => { status; isConnected } (accessors) Live connection status.
useHassConfig () => Accessor<HassConfig | null> Home Assistant config.
useUnitSystem () => Accessor<HassUnitSystem | null> Unit system.
useTemperatureUnit () => Accessor<string> e.g. "°C".
useLocale () => Accessor<string> BCP 47 locale.
useCurrency () => Accessor<string> ISO 4217 currency.

Services

function useService(): {
  callService: ServiceCallFn;
  turnOn:  (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
  turnOff: (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
  toggle:  (entityId: string | string[], data?: Record<string, unknown>) => Promise<void>;
}

type ServiceCallFn = (
  domain: string,
  service: string,
  data?: Record<string, unknown>,
  target?: { entity_id?: string | string[] },
  options?: { returnResponse?: boolean }
) => Promise<unknown>;

Host-mounted widgets route these through the host’s capability check (a control grant is required); preview/tests fall back to direct sync-layer calls. useTurnOn(), useTurnOff(), and useToggle() each return just the one shortcut.

Pass { returnResponse: true } for services that return data, such as todo.get_items; the promise then resolves to Home Assistant’s service response instead of undefined. Only set it for services that support a response, since HA errors otherwise. The capability check is unchanged. Since SDK 1.10.0, and the host forwards the response from Dash 1.1.0-beta.5 onward — on older hosts the call still runs but resolves undefined.

Entity utilities

Export Signature What it is
isEntityActive (entity: EntityView | null | undefined) => boolean true for active states.
getEntityAttribute <T>(entity, name: string, fallback?: T) => T | undefined Read one attribute with a fallback.
hassMediaUrl (path: string | null | undefined) => string | undefined Resolve an HA media path (album art, camera image) to a loadable URL. See Media URLs.
countActiveEntities (entities: EntityView[]) => number Count active entities.
calculateLightGroup (entities: EntityView[], allEntitiesMode?= false) => LightGroupResult Aggregate on/off, brightness, color across a group.
calculateSensorGroup (entities: EntityView[], groupType?: SensorGroupType = "mean", ignoreNonNumeric?= true) => SensorGroupResult Aggregate numeric sensors.

LightGroupResult: isGroup, state, isOn, isUnavailable, brightness (0–255), brightnessPercent (0–100), color, onCount, totalCount, description. SensorGroupResult: isGroup, state, numericValue (number \| null), isUnavailable, unit?, description, memberValues?. SensorGroupType: "min" \| "max" \| "mean" \| "median" \| "sum" \| "last" \| "range" \| "product" \| "std_dev".

Media URLs

Home Assistant exposes media references (album art entity_picture, camera proxy paths) as origin-relative paths like /api/media_player_proxy/...?token=..., which a bare <img src> resolves against the dashboard’s own origin and fails to load. hassMediaUrl resolves them through the host’s same-origin media proxy, so the image loads both at home and over remote access (no extra permissions, the path keeps Home Assistant’s own signed token).

import { hassMediaUrl, useEntity, getEntityAttribute } from "@glasshome/widget-sdk";

const entity = useEntity(entityId);

const albumArt = () => {
  const e = entity();
  if (!e) return undefined;
  return hassMediaUrl(getEntityAttribute<string>(e, "entity_picture"));
};

<Show when={albumArt()}>
  <img src={albumArt()} alt="" />
</Show>;
  • Absolute http(s):// URLs pass through unchanged.
  • Missing or empty input returns undefined, so <Show when={albumArt()}> handles “nothing playing” for free.
  • No external service (cover-art API etc.) is needed for media Home Assistant already serves: read entity_picture and wrap it.
  • Available since widget-sdk 1.3.0.

Theming & charts

Export Signature What it is
isDark () => boolean Current theme as a boolean.
injectTokens (root?: Document | ShadowRoot) => void Attach SDK CSS tokens to a root the host did not set up.
svgColors Record<SvgColorKey, { solid; stroke; fill }> Energy chart colors. Keys: solar, grid, battery, ev, home, positive, negative. See Charts.
ToneSchema Zod enum Validates a Tone.
monotoneCubicPath (points) => string Smooth SVG path string from a list of points.

Tone: "success" \| "warning" \| "danger" \| "info" \| "neutral" \| "accent".

Validation re-exports

Convenience re-exports also available from /schemas.

Export Kind What it is
WidgetManifestSchema Zod schema Validate a complete widget manifest.
formatSchemaError (err) => string Turn a Zod error into a readable message.

Deprecation registry

The single source of truth for deprecated APIs, driving warn-once runtime notices, the CLI lint, and the deprecation docs table.

Export Kind What it is
deprecations DeprecationEntry[] The registry of deprecated APIs and their replacements.
formatDeprecation (entry: DeprecationEntry) => string Render one entry as a readable warning message.
DeprecationEntry type One deprecation record.

Lower-level data exports

Building blocks under the hooks; most widgets never touch these.

Export Kind What it is
state Store<GlassHomeState> The raw reactive store.
getForecasts function Imperative forecast fetch.
trackEntityHistory / untrackEntityHistory (entityId) => void Start / stop history tracking for an entity.
getStream function Camera stream descriptor.
getWebRtcClientConfig function WebRTC client config for camera streaming.
startWebRtcSession function Begin a WebRTC camera session.
sendWebRtcCandidate function Send an ICE candidate during a session.

Types (main entry)

interface WidgetDefinition<C = Record<string, unknown>> {
  manifest: WidgetManifest;
  configSchema?: ZodType<C, unknown>;
  migrate?: (config: Record<string, unknown>, fromConfigVersion: number) => Record<string, unknown>;
  component: (props: { config: C }) => any;
}

interface WidgetManifest {
  name: string;
  description?: string;
  minSize: GridSize;          // { w, h }
  maxSize: GridSize;
  defaultSize?: GridSize;
  sdkVersion: string;
  icon?: string;
  capabilities?: CapabilityGrant[];
  configVersion?: number;
  schema?: object;            // auto-populated from configSchema; do not set
  defaultConfig?: Record<string, unknown>;  // auto-populated; do not set
  cssUrl?: string;            // set by the build
}

interface GridSize { w: number; h: number }

Other exported types: WidgetContext, ReactiveWidgetContext, EntityView (see the shape), AreaView, WidgetVariantConfig, WidgetStyles, Tone, SvgColorKey, GestureHandlers, AggregationPreset, SensorGroupType, LightGroupResult, SensorGroupResult, UseWidgetEntityGroupOptions, UseWidgetEntityGroupResult, WidgetDialogProps, WidgetDialogTab, WidgetDialogReturn, ServiceCallFn.


@glasshome/widget-sdk/schemas

Zod schemas re-exported from the canonical @glasshome/widget-contract package, the single source of truth shared by the SDK, CLI, Hub, and Dash, so the same definitions validate a manifest everywhere.

Export Kind What it is
widgetManifestSchema (alias WidgetManifestSchema) Zod schema Validate a complete widget manifest.
publishManifestSchema Zod schema Validate the manifest subset sent at publish time.
capabilitiesSchema Zod schema Validate a capabilities array.
capabilityGrantSchema Zod schema Validate one { domain, access } grant.
GridSizeSchema Zod schema Validate { w, h }.
parseGridSize / serializeGridSize functions Convert grid size to/from the stored JSON form.
PublishRequestSchema / PublishConfirmSchema / PublishBodySchema Zod schemas Publish-flow request bodies.
formatSchemaError (err) => string Turn a Zod error into a readable message.
CapabilityGrant type One capability grant.

@glasshome/widget-sdk/vite

Build plugins and helpers for vite.config.ts. The scaffolded config wires these; reach for them only in custom setups. See Build tooling.

Export Signature What it is
glasshomeWidget (options?: GlasshomeWidgetOptions) => Plugin[] Single-widget plugin: dev preview + library build.
glasshomeWidgets (options?: GlasshomeWidgetsOptions) => Plugin[] Multi-widget plugin: per-widget builds + registry.json.
buildWidgets (options?: BuildWidgetsOptions) => Promise<void> Programmatic build of all widgets (used by bun widget build).
discoverWidgets (srcDir: string) => DiscoveredWidget[] Scan src/ for subdirs with index.tsx + manifest.json.
generateRegistry (srcDir: string, outDir: string) => void Write a registry.json from discovered manifests.
isWidgetExternal (id: string) => boolean true for host-provided packages (kept out of the bundle).

Option shapes (all exported):

  • GlasshomeWidgetsOptions: { srcDir?, outDir? } only.
  • BuildWidgetsOptions: { srcDir?, outDir?, plugins?, viteConfig?, only? } (plugins, viteConfig, and only are buildWidgets-only; glasshomeWidgets does not accept them).
  • GlasshomeWidgetOptions: { entry? } (defaults to "src/index.tsx").

Dialog UI components (from @glasshome/ui)

WidgetDialog takes its chrome as props. These are not SDK exports; import them from @glasshome/ui/solid and pass them in (keep @glasshome/ui as a dependency). See WidgetDialog component.

ResponsiveDialog, ResponsiveDialogContent, ResponsiveDialogHeader, ResponsiveDialogTitle, ResponsiveDialogDescription, Button, SchemaForm (SchemaForm is required only when you use configSchema).