diff --git a/README.md b/README.md index 7abce2e..c748795 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ The server uses the MCP protocol to share access to a local Home Assistant insta ## Prerequisites -- Node.js 16 or higher -- Yarn package manager +- Node.js 20.10.0 or higher +- NPM package manager - A running Home Assistant instance - A long-lived access token from Home Assistant @@ -25,7 +25,7 @@ The server uses the MCP protocol to share access to a local Home Assistant insta ```bash # Clone the repository -git clone https://github.com/yourusername/homeassistant-mcp.git +git clone https://github.com/jango-blockchained/homeassistant-mcp.git cd homeassistant-mcp # Install dependencies @@ -45,9 +45,9 @@ yarn build Create a `.env` file with: ```env - TOKEN=your_home_assistant_token - BASE_URL=your_home_assistant_url # e.g., http://homeassistant.local:8123 - PORT=3000 # Optional, defaults to 3000 + NODE_ENV=development + HASS_HOST=your_home_assistant_url # e.g., http://homeassistant.local:8123 + HASS_TOKEN=your_home_assistant_token ``` 3. **MCP Client Configuration** @@ -62,120 +62,210 @@ yarn build "/path/to/dist/index.js" ], "env": { - "TOKEN": "your_home_assistant_token", - "BASE_URL": "your_home_assistant_url" + "HASS_TOKEN": "your_home_assistant_token", + "HASS_HOST": "your_home_assistant_url" } } } } ``` -## Supported Commands +## How to Use -### Common Commands (All Entities) +The server provides two main tools for interacting with Home Assistant: -- `turn_on`: Turn entity on -- `turn_off`: Turn entity off -- `toggle`: Toggle entity state +### 1. List Devices Tool -### Light-Specific Commands +Use this tool to discover all available devices and their current states: -- Control brightness (0-255) +```json +{ + "tool": "list_devices" +} +``` - ```json - { - "command": "turn_on", - "entity_id": "light.living_room", - "brightness": 128 +This will return a structured response with all devices grouped by domain: + +```json +{ + "success": true, + "devices": { + "light": [ + { + "entity_id": "light.living_room", + "state": "on", + "attributes": { + "brightness": 128, + "color_temp": 4000, + "friendly_name": "Living Room Light" + } + } + ], + "climate": [ + { + "entity_id": "climate.bedroom", + "state": "heat", + "attributes": { + "temperature": 22, + "hvac_mode": "heat", + "friendly_name": "Bedroom Thermostat" + } + } + ] } - ``` +} +``` -- Set color temperature +### 2. Control Tool - ```json - { - "command": "turn_on", - "entity_id": "light.living_room", - "color_temp": 4000 - } - ``` +Use this tool to control your devices. Here are some common usage examples: -- Set RGB color values +#### Light Control - ```json - { - "command": "turn_on", - "entity_id": "light.living_room", - "rgb_color": [255, 0, 0] - } - ``` +```json +// Turn on a light +{ + "tool": "control", + "command": "turn_on", + "entity_id": "light.living_room" +} -### Cover Commands +// Set brightness +{ + "tool": "control", + "command": "turn_on", + "entity_id": "light.living_room", + "brightness": 128 +} -- `open`: Open cover -- `close`: Close cover -- `stop`: Stop cover movement -- `set_position`: Set cover position (0-100) +// Set color temperature +{ + "tool": "control", + "command": "turn_on", + "entity_id": "light.living_room", + "color_temp": 4000 +} - ```json - { - "command": "set_position", - "entity_id": "cover.living_room", - "position": 50 - } - ``` +// Set RGB color (red) +{ + "tool": "control", + "command": "turn_on", + "entity_id": "light.living_room", + "rgb_color": [255, 0, 0] +} +``` -- `set_tilt_position`: Set cover tilt (0-100) +#### Climate Control - ```json - { - "command": "set_tilt_position", - "entity_id": "cover.living_room", - "tilt_position": 45 - } - ``` +```json +// Set temperature +{ + "tool": "control", + "command": "set_temperature", + "entity_id": "climate.living_room", + "temperature": 22 +} -### Climate Commands +// Set HVAC mode +{ + "tool": "control", + "command": "set_hvac_mode", + "entity_id": "climate.living_room", + "hvac_mode": "heat" +} -- `set_temperature`: Set target temperature +// Set fan mode +{ + "tool": "control", + "command": "set_fan_mode", + "entity_id": "climate.living_room", + "fan_mode": "auto" +} +``` - ```json - { - "command": "set_temperature", - "entity_id": "climate.living_room", - "temperature": 22 - } - ``` +#### Cover Control -- `set_hvac_mode`: Set mode (off, heat, cool, heat_cool, auto, dry, fan_only) +```json +// Open/Close cover +{ + "tool": "control", + "command": "open_cover", // or "close_cover" + "entity_id": "cover.living_room" +} - ```json - { - "command": "set_hvac_mode", - "entity_id": "climate.living_room", - "hvac_mode": "heat" - } - ``` +// Set position +{ + "tool": "control", + "command": "set_position", + "entity_id": "cover.living_room", + "position": 50 +} -- `set_fan_mode`: Set fan mode (auto, low, medium, high) +// Set tilt +{ + "tool": "control", + "command": "set_tilt_position", + "entity_id": "cover.living_room", + "tilt_position": 45 +} +``` - ```json - { - "command": "set_fan_mode", - "entity_id": "climate.living_room", - "fan_mode": "auto" - } - ``` +#### Switch Control -- `set_humidity`: Set target humidity (0-100) +```json +// Turn on/off +{ + "tool": "control", + "command": "turn_on", // or "turn_off" + "entity_id": "switch.office" +} - ```json - { - "command": "set_humidity", - "entity_id": "climate.living_room", - "humidity": 45 - } - ``` +// Toggle +{ + "tool": "control", + "command": "toggle", + "entity_id": "switch.office" +} +``` + +### Error Handling + +The server provides clear error messages when something goes wrong: + +```json +{ + "success": false, + "message": "Failed to execute set_temperature for light.living_room: Unsupported operation for domain: light" +} +``` + +Common error scenarios: +1. Invalid entity ID +2. Unsupported operation for domain +3. Invalid parameter values +4. Home Assistant connection issues + +### Best Practices + +1. **Entity Discovery** + - Always use `list_devices` first to discover available entities + - Note the supported attributes for each device + +2. **Parameter Validation** + - Brightness: 0-255 + - Position/Tilt: 0-100 + - Temperature: Depends on your system's configuration + - Color temperature: Typically 2000-6500K + +3. **Error Recovery** + - If a command fails, check: + - Entity ID exists and is correct + - Command is supported by the domain + - Parameters are within valid ranges + +4. **State Awareness** + - Use `list_devices` to check current state before making changes + - Verify command execution by checking state afterward ## Development @@ -196,7 +286,7 @@ yarn test 1. **Connection Errors** - Verify your Home Assistant instance is running - - Check the BASE_URL is correct and accessible + - Check the HASS_HOST is correct and accessible - Ensure your token has the required permissions 2. **Entity Control Issues** @@ -211,7 +301,6 @@ yarn test ## Project Status ### Completed - - [x] Access to entities - [x] Access to Floors - [x] Access to Areas @@ -224,7 +313,6 @@ yarn test - [x] Switches ### In Progress - - [ ] Testing / writing custom prompts - [ ] Testing using resources for high-level context - [ ] Test varying tool organization @@ -248,3 +336,164 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file - [Model Context Protocol Documentation](https://modelcontextprotocol.io/introduction) - [Home Assistant Documentation](https://www.home-assistant.io) - [Home Assistant REST API](https://developers.home-assistant.io/docs/api/rest) + +## Using with LLMs (AI Assistants) + +The MCP server is designed to work seamlessly with AI language models. Here's how to interact with your Home Assistant using natural language: + +### Natural Language Examples + +1. **Discovering Devices** + ``` + "What devices do I have in my home?" + "Show me all my lights" + "List the climate controls in the bedroom" + ``` + The LLM will use the `list_devices` tool to fetch and present this information in a human-readable format. + +2. **Basic Controls** + ``` + "Turn on the living room lights" + "Set the bedroom temperature to 22 degrees" + "Close all the blinds" + ``` + The LLM will translate these commands into appropriate tool calls using the `control` tool. + +3. **Complex Operations** + ``` + "Make the living room cozy for movie night" + → LLM might: + - Dim the lights (set brightness to 30%) + - Set warm color temperature + - Lower the blinds + - Adjust the temperature + + "Set up my morning routine" + → LLM might: + - Open the bedroom blinds + - Turn on specific lights + - Adjust the thermostat + ``` + +4. **State-Aware Commands** + ``` + "Is my front door closed?" + "Which lights are currently on?" + "What's the temperature in the bedroom?" + ``` + The LLM will check current states using `list_devices` before responding. + +### Context and Memory + +The LLM can maintain context across multiple interactions: + +``` +User: "How warm is it in the bedroom?" +LLM: [checks temperature] "The bedroom is currently 20°C" +User: "Make it a bit warmer" +LLM: [remembers context, adjusts by reasonable increment] "I'll increase it to 22°C" +``` + +### Natural Parameter Handling + +The LLM can interpret natural language into specific parameters: + +``` +"Make the lights very dim" → brightness: 10% +"Set a comfortable temperature" → temperature: 21-23°C +"Change the lights to a warm color" → color_temp: ~2700K +``` + +### Intelligent Error Prevention + +The LLM will: +1. Validate commands before execution +2. Check device capabilities +3. Ensure parameters are within acceptable ranges +4. Provide helpful feedback if a command can't be executed + +Example: +``` +User: "Set the kitchen light to blue" +LLM: [checks if the light supports RGB] +- If supported: Sets rgb_color to [0, 0, 255] +- If not supported: "I'm sorry, but your kitchen light doesn't support color changes. I can only adjust its brightness." +``` + +### Best Practices for LLM Interactions + +1. **Be Specific with Locations** + - Good: "Turn on the kitchen lights" + - Better: "Turn on the lights above the kitchen counter" + +2. **Use Natural Increments** + - "Make it a little brighter" → +20% brightness + - "Make it much warmer" → +3-4°C + +3. **Group Related Commands** + ``` + "Set up the living room for watching TV: + - Dim the lights to 20% + - Set them to a warm color + - Lower the blinds + - Set the temperature to 22 degrees" + ``` + +4. **Ask for Confirmation** + ``` + User: "Turn off all lights" + LLM: "I'll turn off all 12 lights in your home. Would you like me to proceed?" + ``` + +### Handling Complex Scenarios + +1. **Conditional Commands** + ``` + "If the temperature is above 25°C, turn on the fan" + → LLM will: + 1. Check current temperature + 2. Execute command if condition is met + ``` + +2. **Time-Based Context** + ``` + "Set up my evening lighting" + → LLM considers: + - Time of day + - Current light levels + - User preferences + ``` + +3. **Multi-Room Coordination** + ``` + "Prepare the house for bedtime" + → LLM orchestrates: + - Turning off main living area lights + - Dimming hallway lights + - Setting night mode temperatures + - Ensuring doors are locked + ``` + +### Troubleshooting with LLMs + +The LLM can help diagnose issues: +``` +User: "The living room lights aren't responding" +LLM: Let me check: +1. Verifies device availability +2. Checks current state +3. Reviews recent commands +4. Suggests potential solutions +``` + +### Security Considerations + +1. **Confirmation for Critical Actions** + - The LLM will ask for confirmation before: + - Controlling security devices + - Making large temperature changes + - Executing commands affecting multiple devices + +2. **Permission Awareness** + - The LLM respects device permissions + - Provides clear feedback when actions aren't permitted diff --git a/src/__tests__/context.test.ts b/src/__tests__/context.test.ts index 0637045..153c940 100644 --- a/src/__tests__/context.test.ts +++ b/src/__tests__/context.test.ts @@ -4,6 +4,7 @@ import { DomainSchema } from '../schemas.js'; // Define types for tool and server interface Tool { name: string; + description: string; execute: (params: any) => Promise; parameters: z.ZodType; } diff --git a/src/index.ts b/src/index.ts index 0acf388..89543f9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,6 +34,51 @@ async function main() { // Create MCP server const server = new LiteMCP('home-assistant', '0.1.0'); + // Add the list devices tool + server.addTool({ + name: 'list_devices', + description: 'List all available Home Assistant devices', + parameters: z.object({}), + execute: async () => { + try { + const response = await fetch(`${process.env.HASS_HOST}/api/states`, { + headers: { + Authorization: `Bearer ${process.env.HASS_TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`Failed to fetch devices: ${response.statusText}`); + } + + const states = await response.json(); + const devices = states.reduce((acc: any, state: any) => { + const domain = state.entity_id.split('.')[0]; + if (!acc[domain]) { + acc[domain] = []; + } + acc[domain].push({ + entity_id: state.entity_id, + state: state.state, + attributes: state.attributes, + }); + return acc; + }, {}); + + return { + success: true, + devices, + }; + } catch (error) { + return { + success: false, + message: error instanceof Error ? error.message : 'Unknown error occurred', + }; + } + }, + }); + // Add the Home Assistant control tool server.addTool({ name: 'control', @@ -137,6 +182,7 @@ async function main() { default: throw new Error(`Unsupported operation for domain: ${domain}`); } + // Call Home Assistant service try { await hass.services[domain][service](serviceData);