Files

77 lines
3.3 KiB
TypeScript

// home-assistant's tools — its own code (novox/hq ADR 0039), importing its own client. They return
// structured data; the mesh serves them through the sdk's tool harness.
import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools";
import { HomeAssistantClient, type HAEntityState } from "../client.js";
/** Trim an entity to the fields worth returning — the full attribute bag is large and mostly noise. */
function summarize(s: HAEntityState): { entity_id: string; state: string; name?: string; last_changed?: string } {
return {
entity_id: s.entity_id,
state: s.state,
name: typeof s.attributes.friendly_name === "string" ? s.attributes.friendly_name : undefined,
last_changed: s.last_changed,
};
}
export function getHomeAssistantTools(ha: HomeAssistantClient): ToolDefinition[] {
return [
{
name: "homeassistant_states",
description:
"Entity states from Home Assistant. With no argument, lists every entity; with `entity` (e.g. light.kitchen), returns just that one with its full attributes.",
input: { entity: { type: "string", description: "an entity id to fetch one entity; omitted lists all" } },
run: async (args) => {
if (args.entity) {
const s = await ha.getState(String(args.entity));
return { entity_id: s.entity_id, state: s.state, attributes: s.attributes, last_changed: s.last_changed };
}
const states = await ha.getStates();
return { count: states.length, entities: states.map(summarize) };
},
},
{
name: "homeassistant_call_service",
description:
"Call a Home Assistant service — turn a switch/light on or off, lock a door, etc. Give the domain (e.g. switch), the service (e.g. turn_on), and optionally a target entity and extra data.",
input: {
domain: { type: "string", description: "the service domain, e.g. light, switch, lock" },
service: { type: "string", description: "the service, e.g. turn_on, turn_off, toggle" },
entity: { type: "string", description: "the entity id to target (optional)" },
data: { type: "object", description: "extra service data merged into the call (optional)" },
},
run: async (args) => {
const data: Record<string, unknown> = { ...(args.data as Record<string, unknown> | undefined) };
if (args.entity) data.entity_id = String(args.entity);
const changed = await ha.callService(String(args.domain), String(args.service), data);
return { changed: changed.map(summarize) };
},
},
{
name: "homeassistant_config",
description: "Home Assistant instance config: version, location, timezone, loaded components.",
input: {},
run: async () => {
const c = await ha.getConfig();
return {
location: c.location_name,
version: c.version,
time_zone: c.time_zone,
state: c.state,
components: c.components?.length ?? 0,
};
},
},
];
}
// The tools exist only when a token is configured; without one, home-assistant contributes none
// rather than failing the whole runtime.
registerModuleTools("home-assistant", (env) => {
try {
return getHomeAssistantTools(HomeAssistantClient.fromEnv(env));
} catch {
return [];
}
});