Read and control a Home Assistant instance — entity states, service calls, history, templates, calendars and events.
Home Assistant ships in the w6w first-party pack. It declares 19 actions, 4 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.homeassistantHome Assistant is a home automation hub you run yourself, and this app reads and controls a running instance: entity states, the services that actually operate devices, historical state changes, calendars, and events. It is built to automate a smart home from a workflow — checking whether a door is locked, turning on a light, or reading what a sensor recorded overnight — while steering around the sharp edges of an API built for the home rather than for automation software talking to it over the internet.
The most important of those edges is that setting an entity’s state directly does not control the underlying device; only a service call or the dedicated on/off/toggle action does that, and this app refuses the former on device-backed entities unless explicitly told otherwise. It also renders Home Assistant’s own templating language against live state, fires custom events to trigger automations, and can validate a configuration file before a restart that would otherwise risk leaving the instance down.
Three routes to the same 19 actions. The Workflow tab is generated from Home Assistant's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
calendar-events Events in a time window. All-day events use `start.date` and timed ones `start.dateTime` — never both — so this normalises them and flags which is which.
calendar-list Calendar entities on this instance. Home Assistant normalises Google, CalDAV and local calendars into one shape, so this is one API for all of them.
camera-snapshot The latest still from a camera, as base64. This may be an old frame — the proxy returns whatever the integration last received, and nothing says when.
config-check Validate configuration.yaml without restarting. A syntax error does not stop a running instance — it stops it coming back, so check before restarting.
config-get Version, time zone, unit system and loaded components. The unit system is instance-wide and decides what every temperature sensor's numbers mean.
entity-switch Turn things on, off, or toggle them. Uses the domain-agnostic services, so one action works for lights, switches, fans, scripts and scenes alike.
error-log Plain-text log since the last restart — the only place that explains WHY an entity is unavailable. Returns the tail, because a retrying integration can fill megabytes.
event-fire Put a custom event on Home Assistant's bus — the clean way to trigger an automation from outside. Nothing reports whether anything listened.
event-list Event types with their listener counts — the only way to find out whether anything is listening before firing, since firing always reports success.
history-get Recorded state changes for named entities. Asking without naming entities would query every entity on the instance, so this requires them.
intent-handle Do something by INTENT rather than by entity id — Home Assistant resolves 'kitchen light' the way the voice assistant does. Right for human input, wrong when the entity is known.
logbook-get Human-readable events with their causes — which automation or person triggered them. History says what changed; the logbook says why.
service-call Do something — this is the endpoint that controls devices, unlike `state-set`. A 200 with no changed states is normal and is not confirmation that anything happened.
service-list What this instance can actually do. Services come from installed integrations, so the list is per-instance — there is no universal catalogue.
state-delete Remove an entity from the state machine. Only meaningful for entities pushed in with `state-set` — anything with an integration behind it is recreated on the next update.
state-get One entity's state and attributes. The state is ALWAYS a string — including `unavailable` and `unknown`, which parse to NaN — so this reports whether the value is usable.
state-list Every entity and its state. Home Assistant has NO server-side filter, so this is often megabytes — filtering here narrows the result, not the transfer.
state-set Set what Home Assistant BELIEVES an entity's state is. This does NOT communicate with any device — to turn something on, use `service-call`. For pushing outside values in.
template-render Evaluate a Jinja2 template against live state — the whole instance queried server-side in one call. Returns PLAIN TEXT, so a list comes back as Python repr rather than JSON.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying Home Assistant's real ids.
{
"manifestVersion": "2",
"name": "homeassistant-example",
"steps": [
{
"id": "calendar-events",
"uses": {
"app": "io.w6w.homeassistant",
"action": "calendar-events",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"entityId": "<entityId>",
"start": "<start>",
"end": "<end>"
}
}
]
}calendar-events calendar-list camera-snapshot config-check config-get +14 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for Home Assistant, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to Home Assistant. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "calendar-events",
payload: {
entityId: "<value>",
start: "<value>",
end: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action calendar-events --payload '{"entityId":"<value>","start":"<value>","end":"<value>"}' Give an AI agent Home Assistant — without giving it Home Assistant's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.homeassistant#calendar-events",
"input": {
"entityId": "<entityId>",
"start": "<start>",
"end": "<end>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's Home Assistant connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
Home Assistant's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. Home Assistant itself is MIT, and the runtime that executes it is source-available (FSL).
Home Assistant declares its own checks, so its health is a property of the app rather than something the host guesses at.
Status of Home Assistant's own infrastructure and Nabu Casa Cloud. It says NOTHING about your instance — but Remote UI outages are why a working instance becomes unreachable.
Whether this Home Assistant answers and has finished starting. A restarting instance serves the API for minutes before its integrations are loaded, during which everything reads unavailable.
What proportion of entities read `unavailable` or `unknown`. A broken integration raises nothing — its entities just stop having values, and threshold comparisons silently go false.