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
JavaScript
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"
};
}
}
}