Skip to content

Repository files navigation

Developer Shell for Home Assistant

A slice of cherry pie, drawn like a 1990s Visual Basic icon

Home Assistant environment designed for custom component developers and tinkerers. Makes it easy as pie!

Its aims are:

  • exploring of the APIs in context of a live working instance
  • trialling out snippets of code
  • debugging code
  • hotfixing issues that don't have built in support to do so from existing components.

This is primarily for developers of custom components, and their agents, though may be of interest for other folk tinkering with Home Assistant. It is a potentially sharp tool, so NOT appropriate for general Home Assistant users.

Back End

A HACS component that taps into the Home Assistant and acts as a session server over web sockets.

Object Browser

All of the objects in Home Assistant - entities, devices, actions, areas, labels etc - are represented live in an Object Explorer, a tree structure, nested by the "." split name and with the object type and integration_key at the root. The properties of objects can be inspected.

Logical

 - <integration>
   - <domain>
     - <id>

Physical

 - alexa_devices
   - media_player
     - kitchen_show
   - notify
     - bedroom_echo
   - action
     - play_sound
   - device # maps to a device config entry, same physical device may have several
     - 9949349049030434348434

The object browser makes the largely correct assumption that everything useful lives in an integration

Enhancements

  • Add button re-using Home Assistant dialogs
  • Delete, with multi-select, calling regular home-assistant checks
  • Copy and paste, similar to how it works in front end for duping automations etc
  • Filter by area/label/floor/category/platform/domain/free text
  • Handle legacy yaml entities without a unique_id
  • Actions that don't live in integrations, and other orphans

REPL

The REPL shell is a full Python REPL shell, implemented as a VSCode NotebookController, with multi-line editing, history etc, living inside an asyncio loop that exposes the live Home Assistant instance as

  • hass - the HomeAssistant class at the root of the Python API
  • obj[] - the object tree exposed as a dictionary object

So I can write code at the command line like:

pir=obj["/rflink/binary_sensor/hall_pir"]
pir.state="on" # non-strict mode, sets entity state with repl as context

The return value of the object is returned to the shell, value printed and available to Python code as _. Tracebacks are printed also, as if they were local (in general everything feels like its local)

There is another special object, this which is the object currently highlighted in the Object Browser.

Strict Mode

This switch controls trade-off between shell convenience and ability to trial working code for a component.

  • Control to switch it off in the toolbar of the REPL.
  • on
    • Access to the core API is exactly as it would be in regular component code.
  • off
    • Subset of methods that don't require arguments can be accessed like properties, even if they aren't defined that way, and very common ones, like state are auto-wired to be gettable and settable.
    • async methods are automatically awaited
    • 'RegistryEntityandEntitymerged,.hass` stripped

Enhancements

  • Drop down button to switch context between different Home Assistant instances
  • Read-only Mode
    • Control to switch it off in the toolbar of the REPL.
    • Caveat about side-effects of supposedly read-only calls and difficulty of knowing what's really read-only. Possibly blanket ban on calling actions.
  • Click thru from stacktrace to hass code
  • Intellisense, e.g. pop up attribute suggestions when pressing .
  • Auto clone the source of current HA instance and keep tag aligned with release
    • On demand also for custom components, both this and core can be disabled for space constrained devices, with inspect.getsource as backup

Other Ideas

  • Notebook support, e.g. Marimo
  • CLI
  • safe(r) mode - remove things like stop
  • Event subscription and visualization
  • LSP?
  • Everything runs in the async loop, non async code wrapped as async in HA compatible way. Warn about blocking calls in non-strict
  • Persisting context across invocations
  • Appdaemon support
  • Multiple sessions with a session switcher and optional names
  • Ability to reload custom component classes within the shell reload(x) for fast feedback debug/development
  • Redirect log statements captured
  • Tight authN / authZ / encryption

Limitations

  • NOT a debugger, but may do more to complement one
  • NOT pyscript, focuses on actual python even at expense of general usability or home assistance access, no script execution support outside of the repl

MVP

  • Basic REPL
    • strict mode only
    • hass binding only (entities accessible via usual hass calls)
    • packaged as CLI with exec and interative mode, session name and session reset flag and HASS_URL
  • Object browser
    • out of scope
    • so also no this or obj[]
  • VSCode integration
    • out of scope
  • HACS Component
    • server side session management (session reused indefinitely until reset)
    • stdout and traceback reflection to shell
    • admin-only websocket
    • clean up sessions after x hours

Development

Layout:

  • custom_components/dev_shell_server/: the HACS integration (named dev_shell_server to distinguish it from the CLI below). session.py is the execution engine (no HA imports); websocket_api.py exposes the admin-only dev_shell_server/exec, dev_shell_server/reset and dev_shell_server/sessions commands.
  • src/dev_shell/: the dev_shell CLI (exec, interactive REPL, reset, sessions).
  • dev/: a throwaway HA config with demo: entities, plus run and bootstrap scripts.

Dev instance (devcontainer, or directly on a host with Python 3.14):

dev/setup.sh                 # installs HA into its own venv + the CLI (devcontainer runs this)
dev/run-ha.sh                # HA on :8123 with custom_components/dev_shell_server symlinked in
uv run python dev/bootstrap.py   # onboard (user dev/dev) and write dev/.env with a token

Using it (host or container):

set -a; . dev/.env; set +a
uv run dev_shell                                         # interactive
uv run dev_shell exec 'hass.states.get("sun.sun")'
uv run dev_shell exec - <<'PY'
await hass.services.async_call("light", "toggle", {"entity_id": "light.kitchen_lights"}, blocking=True)
hass.states.get("light.kitchen_lights").state
PY

On a real instance, install via HACS and add Developer Shell for Home Assistant from Settings → Devices & services → Add integration (or add dev_shell_server: to configuration.yaml, which is imported as a config entry), then set HASS_URL and HASS_TOKEN (an admin long-lived token).

Tests: uv run pytest covers the engine without needing HA.

About

Developer Shell for Home Assistant

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages