UNPKG

domotz-mcp

Version:

MCP server for Domotz API and BMS (Building Management System) integration with IoT device monitoring

132 lines (131 loc) 5.06 kB
import axios from "axios"; import { z } from "zod"; import { MCPTool } from "mcp-framework"; export default class HomeAssistantEntityTool extends MCPTool { name = "ha_control"; description = "Control Home Assistant entities (lights, switches, climate, etc.)"; schema = { ha_url: { type: z.string(), description: "Home Assistant URL (e.g., http://homeassistant.local:8123)" }, access_token: { type: z.string(), description: "Long-lived access token from Home Assistant" }, entity_id: { type: z.string(), description: "Entity ID to control (e.g., light.living_room, switch.garage)" }, action: { type: z.string(), description: "Action to perform (turn_on, turn_off, toggle, set_temperature, etc.)" }, service_data: { type: z.string().optional(), description: "Additional service data as JSON string (e.g., {\"brightness\": 255, \"color_name\": \"blue\"})" } }; async execute(params) { try { const { ha_url, access_token, entity_id, action, service_data } = params; // Remove trailing slash from URL const baseUrl = ha_url.replace(/\/$/, ""); // Determine domain and service from entity_id and action const domain = entity_id.split('.')[0]; let service = action; // Map common actions to services const actionMap = { light: { on: "turn_on", off: "turn_off", toggle: "toggle" }, switch: { on: "turn_on", off: "turn_off", toggle: "toggle" }, climate: { set_temperature: "set_temperature", set_hvac_mode: "set_hvac_mode", turn_on: "turn_on", turn_off: "turn_off" }, cover: { open: "open_cover", close: "close_cover", stop: "stop_cover", set_position: "set_cover_position" }, media_player: { play: "media_play", pause: "media_pause", stop: "media_stop", volume_set: "volume_set" } }; // Use mapped service if available if (actionMap[domain] && actionMap[domain][action]) { service = actionMap[domain][action]; } // Prepare service data let serviceDataObj = { entity_id }; if (service_data) { try { const parsedData = JSON.parse(service_data); serviceDataObj = { ...serviceDataObj, ...parsedData }; } catch (e) { return { success: false, error: "Invalid service_data JSON format" }; } } // Call the service const response = await axios.post(`${baseUrl}/api/services/${domain}/${service}`, serviceDataObj, { headers: { "Authorization": `Bearer ${access_token}`, "Content-Type": "application/json" } }); // Get the updated state const stateResponse = await axios.get(`${baseUrl}/api/states/${entity_id}`, { headers: { "Authorization": `Bearer ${access_token}`, "Content-Type": "application/json" } }); return { success: true, message: `Successfully executed ${service} on ${entity_id}`, service_called: `${domain}.${service}`, current_state: { entity_id: stateResponse.data.entity_id, state: stateResponse.data.state, attributes: stateResponse.data.attributes, last_changed: stateResponse.data.last_changed } }; } catch (error) { if (error.response?.status === 401) { return { success: false, error: "Invalid access token. Please check your Home Assistant long-lived access token." }; } if (error.response?.status === 404) { return { success: false, error: "Entity not found. Please check the entity_id." }; } return { success: false, error: error.response?.data?.message || error.message || "Failed to control Home Assistant entity" }; } } }