Skip to content

Updated

View MarkdownOpen in ChatGPTOpen in Claude

Getting Started

A widget is a SolidJS component + a Zod config schema, wrapped with defineWidget. The dashboard renders the component, auto-generates the settings form from the schema, and feeds in entity state.

Scaffold

`bunx @glasshome/widget-cli@latest` (no args). Creates a project and your first widget.

Hot-reload

`bun widget connect <dashboard-url>` rebuilds on save and re-uploads to a running dashboard.

Publish

`bun widget publish` to Hub. Pick personal or organization scope.

Inside a scaffolded project, bun widget <cmd> works (the CLI is pinned in devDependencies, with a widget script alias to glasshome-widget); elsewhere use bunx @glasshome/widget-cli@latest <cmd>. Full command list: Widget CLI.

Prerequisites

Tool Version Notes
Bun 1.0+ CLI engines field requires bun >=1.0.0
Node.js 18+ Required by @glasshome/widget-sdk (engines.node >= 18); Bun’s bundled Node compatibility layer satisfies this

Scaffolded projects ship a .mise.toml pinning the versions. Install mise and run mise install.

Quickstart

  1. 1

    Scaffold a project

    bunx @glasshome/widget-cli@latest
    cd <project-name>

    Generates package.json, vite.config.ts, tsconfig.json, the widget script alias, and your first widget (prompts for name + description).

  2. 2

    (Optional) add more widgets

    bun widget add

    Prompts for name and description, creates src/<name>/index.tsx and src/<name>/manifest.json.

  3. 3

    Connect to a running Dash instance

    bun widget connect http://homeassistant.local:3123

    Builds, runs an OAuth device-code flow against your local Dash instance (not Hub), uploads each bundle to the Dash API under the local scope, enables dev mode, then watches src/, re-uploading on save; widgets refresh without a page reload.

  4. 4

    Publish

    bun widget publish

    publish runs login automatically the first time. See Publishing.

Project layout

my-widgets/
├── package.json
├── vite.config.ts
├── tsconfig.json
└── src/
    ├── my-widget/
    │   ├── index.tsx          # Component + defineWidget
    │   └── manifest.json      # Widget metadata
    └── another-widget/
        ├── index.tsx
        └── manifest.json

One widget per directory under src/. Build outputs one self-contained JS bundle per widget plus a registry.json in dist/.

Mental model

  • The dashboard handles rendering, gestures, theming, entity subscriptions, settings forms, and layout. Your widget supplies how the bound entities are visualized and what the primary tap does.
  • No settings UI to write: define a Zod schema; the dashboard renders the form.
  • No WebSocket state to manage: useEntity / useEntities read the entity ids the user picked in config and stay reactive; useWidgetEntityGroup aggregates them and supplies an empty state.
  • No Home Assistant connection or token to hold: all reads and service calls go through SDK hooks, checked against your declared capabilities.

Versions at a glance

Version type Where it lives Who bumps it What breaks without it
SDK version (sdkVersion) manifest.json, as a semver range (e.g. ^1.0.0) bun widget upgrade (bumps the dep and rewrites the range) Dash may refuse to load bundles built against a wildly different SDK
Widget version (version) manifest.json --bump flag on bun widget publish Re-publishing the same version is rejected (409)
Config version (configVersion) manifest.json, integer You, when config shape has breaking changes Without a bump, users’ saved configs aren’t migrated and may fail Zod parse
CLI minimum (Hub-enforced) Served by Hub at /api/widgets/cli-version Hub (I bump it on protocol changes) login/publish hard-stop if CLI is below the floor

Auth flows: connect vs. login

Both flows open a browser, but they authenticate against different hosts, and the resulting tokens are stored separately, never interchangeable:

Where to go next