Skip to content

Updated

View MarkdownOpen in ChatGPTOpen in Claude

Capabilities & Permissions

A widget runs sandboxed: an isolated shadow root, no Home Assistant connection or token, no network beyond Home Assistant and the Hub. To read or control anything in the home, it declares the access it needs in its manifest. The user approves that list at install, and the host enforces it on every call.

Access is deny-by-default: anything you don’t declare is blocked at runtime.

This is the developer’s view. For the homeowner-facing explanation of the sandbox, see Widget Security.

How access works

Home Assistant interaction goes only through the SDK’s data and service hooks: importing @glasshome/sync-layer directly fails the build, and fetch/WebSocket to Home Assistant is blocked. The host checks every call against your declared capabilities before touching Home Assistant.

Reads (entity state, history, forecasts) require a read grant for the domain; service calls require control.

Declaring capabilities

  1. 1

    List what the widget needs, minimally

    Decide which Home Assistant domains the widget touches and whether it only shows them (read) or also operates them (control). Ask for the least that works.

  2. 2

    Add a capabilities array to the manifest

    Each grant is { domain, access }, plus optional narrowing:

    // manifest.json
    "capabilities": [
      { "domain": "light", "access": "control" },
      { "domain": "sensor", "access": "read" }
    ]

    The same array goes in the inline manifest passed to defineWidget. A widget that touches no Home Assistant data (a clock, say) declares an empty array:

    "capabilities": []

    Targeting SDK 1.x without a capabilities array is rejected at publish. A manifest may declare at most 32 grants.

  3. 3

    Use the data layer

    Read and control through the SDK hooks:

    import { useEntity, useService } from "@glasshome/widget-sdk";
    
    const light = useEntity(() => props.config.entityId);   // needs read/control on "light"
    const { toggle } = useService();
    // toggle(props.config.entityId)                          // needs control on "light"

Access levels

access Grants Use it for
read Reading state, attributes, history, forecasts for the domain Widgets that only display
control Read and calling services on the domain Widgets that operate devices

control implies read on the same domain, so you never need both.

Narrowing a grant

A grant covers a whole domain by default; tighten it when the widget needs only part of one.

To specific entities

entities lists entity-id patterns. The domain part stays literal and * globs only the object id, so a pattern can never widen a grant past its domain.

{ "domain": "light", "access": "control", "entities": ["light.living_*", "light.kitchen"] }

An entity-scoped grant cannot reach domain-wide services (a service call with no entity target). Leave entities off if the widget needs the whole domain.

To specific services

services restricts a control grant to named services; calls outside the list are rejected.

{ "domain": "media_player", "access": "control", "services": ["media_play", "media_pause"] }

entities and services must be non-empty when present.

What the user approves

The install consent screen renders each grant as a plain-language sentence, generated from the same object the host enforces, so the prompt cannot drift from what is allowed:

Grant Shown to the user
{ domain: "light", access: "control" } Control your lights
{ domain: "sensor", access: "read" } Read your sensors
{ domain: "light", access: "control", entities: ["light.living_*"] } Control your lights (light.living_*)
{ domain: "media_player", access: "control", services: ["media_play","media_pause"] } Control your media players — only: media play, media pause

Enforcement and updates

  • Deny-by-default. Every read and service call is checked against your grants at runtime; anything unmatched is blocked and the user is notified. Widget code cannot opt out.
  • Widening re-prompts. An update asking for more than the user approved (a new domain, broader access, removed narrowing) must be re-approved before it runs. Narrowing or unchanged grants install silently.
  • Verified on publish. The Hub validates your capability grammar and the bundle hash before a version goes live.

See also