docs: expand documentation with comprehensive tools and development guides
- Add detailed documentation for various tools and management interfaces - Create development best practices and interface documentation - Expand tools section with device management, automation, and event subscription guides - Include configuration, usage examples, and error handling for each tool - Update MkDocs navigation to reflect new documentation structure
This commit is contained in:
310
docs/development/best-practices.md
Normal file
310
docs/development/best-practices.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# Development Best Practices
|
||||
|
||||
This guide outlines the best practices for developing tools and features for the Home Assistant MCP.
|
||||
|
||||
## Code Style
|
||||
|
||||
### TypeScript
|
||||
|
||||
1. Use TypeScript for all new code
|
||||
2. Enable strict mode
|
||||
3. Use explicit types
|
||||
4. Avoid `any` type
|
||||
5. Use interfaces over types
|
||||
6. Document with JSDoc comments
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Represents a device in the system.
|
||||
* @interface
|
||||
*/
|
||||
interface Device {
|
||||
/** Unique device identifier */
|
||||
id: string;
|
||||
|
||||
/** Human-readable device name */
|
||||
name: string;
|
||||
|
||||
/** Device state */
|
||||
state: DeviceState;
|
||||
}
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
1. Use PascalCase for:
|
||||
- Classes
|
||||
- Interfaces
|
||||
- Types
|
||||
- Enums
|
||||
|
||||
2. Use camelCase for:
|
||||
- Variables
|
||||
- Functions
|
||||
- Methods
|
||||
- Properties
|
||||
|
||||
3. Use UPPER_SNAKE_CASE for:
|
||||
- Constants
|
||||
- Enum values
|
||||
|
||||
```typescript
|
||||
class DeviceManager {
|
||||
private readonly DEFAULT_TIMEOUT = 5000;
|
||||
|
||||
async getDeviceState(deviceId: string): Promise<DeviceState> {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### SOLID Principles
|
||||
|
||||
1. Single Responsibility
|
||||
- Each class/module has one job
|
||||
- Split complex functionality
|
||||
|
||||
2. Open/Closed
|
||||
- Open for extension
|
||||
- Closed for modification
|
||||
|
||||
3. Liskov Substitution
|
||||
- Subtypes must be substitutable
|
||||
- Use interfaces properly
|
||||
|
||||
4. Interface Segregation
|
||||
- Keep interfaces focused
|
||||
- Split large interfaces
|
||||
|
||||
5. Dependency Inversion
|
||||
- Depend on abstractions
|
||||
- Use dependency injection
|
||||
|
||||
### Example
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
class DeviceManager {
|
||||
async getState() { /* ... */ }
|
||||
async setState() { /* ... */ }
|
||||
async sendNotification() { /* ... */ } // Wrong responsibility
|
||||
}
|
||||
|
||||
// Good
|
||||
class DeviceManager {
|
||||
constructor(
|
||||
private notifier: NotificationService
|
||||
) {}
|
||||
|
||||
async getState() { /* ... */ }
|
||||
async setState() { /* ... */ }
|
||||
}
|
||||
|
||||
class NotificationService {
|
||||
async send() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. Use custom error classes
|
||||
2. Include error codes
|
||||
3. Provide meaningful messages
|
||||
4. Include error context
|
||||
5. Handle async errors
|
||||
6. Log appropriately
|
||||
|
||||
```typescript
|
||||
class DeviceError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public context: Record<string, any>
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'DeviceError';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await device.connect();
|
||||
} catch (error) {
|
||||
throw new DeviceError(
|
||||
'Failed to connect to device',
|
||||
'DEVICE_CONNECTION_ERROR',
|
||||
{ deviceId: device.id, attempt: 1 }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Guidelines
|
||||
|
||||
1. Write unit tests first
|
||||
2. Use meaningful descriptions
|
||||
3. Test edge cases
|
||||
4. Mock external dependencies
|
||||
5. Keep tests focused
|
||||
6. Use test fixtures
|
||||
|
||||
```typescript
|
||||
describe('DeviceManager', () => {
|
||||
let manager: DeviceManager;
|
||||
let mockDevice: jest.Mocked<Device>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDevice = {
|
||||
id: 'test_device',
|
||||
getState: jest.fn()
|
||||
};
|
||||
manager = new DeviceManager(mockDevice);
|
||||
});
|
||||
|
||||
it('should get device state', async () => {
|
||||
mockDevice.getState.mockResolvedValue('on');
|
||||
const state = await manager.getDeviceState();
|
||||
expect(state).toBe('on');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Optimization
|
||||
|
||||
1. Use caching
|
||||
2. Implement pagination
|
||||
3. Optimize database queries
|
||||
4. Use connection pooling
|
||||
5. Implement rate limiting
|
||||
6. Batch operations
|
||||
|
||||
```typescript
|
||||
class DeviceCache {
|
||||
private cache = new Map<string, CacheEntry>();
|
||||
private readonly TTL = 60000; // 1 minute
|
||||
|
||||
async getDevice(id: string): Promise<Device> {
|
||||
const cached = this.cache.get(id);
|
||||
if (cached && Date.now() - cached.timestamp < this.TTL) {
|
||||
return cached.device;
|
||||
}
|
||||
|
||||
const device = await this.fetchDevice(id);
|
||||
this.cache.set(id, {
|
||||
device,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
return device;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Guidelines
|
||||
|
||||
1. Validate all input
|
||||
2. Use parameterized queries
|
||||
3. Implement rate limiting
|
||||
4. Use proper authentication
|
||||
5. Follow OWASP guidelines
|
||||
6. Sanitize output
|
||||
|
||||
```typescript
|
||||
class InputValidator {
|
||||
static validateDeviceId(id: string): boolean {
|
||||
return /^[a-zA-Z0-9_-]{1,64}$/.test(id);
|
||||
}
|
||||
|
||||
static sanitizeOutput(data: any): any {
|
||||
// Implement output sanitization
|
||||
return data;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### Standards
|
||||
|
||||
1. Use JSDoc comments
|
||||
2. Document interfaces
|
||||
3. Include examples
|
||||
4. Document errors
|
||||
5. Keep docs updated
|
||||
6. Use markdown
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Manages device operations.
|
||||
* @class
|
||||
*/
|
||||
class DeviceManager {
|
||||
/**
|
||||
* Gets the current state of a device.
|
||||
* @param {string} deviceId - The device identifier.
|
||||
* @returns {Promise<DeviceState>} The current device state.
|
||||
* @throws {DeviceError} If device is not found or unavailable.
|
||||
* @example
|
||||
* const state = await deviceManager.getDeviceState('living_room_light');
|
||||
*/
|
||||
async getDeviceState(deviceId: string): Promise<DeviceState> {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. Use appropriate levels
|
||||
2. Include context
|
||||
3. Structure log data
|
||||
4. Handle sensitive data
|
||||
5. Implement rotation
|
||||
6. Use correlation IDs
|
||||
|
||||
```typescript
|
||||
class Logger {
|
||||
info(message: string, context: Record<string, any>) {
|
||||
console.log(JSON.stringify({
|
||||
level: 'info',
|
||||
message,
|
||||
context,
|
||||
timestamp: new Date().toISOString(),
|
||||
correlationId: context.correlationId
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Version Control
|
||||
|
||||
### Guidelines
|
||||
|
||||
1. Use meaningful commits
|
||||
2. Follow branching strategy
|
||||
3. Write good PR descriptions
|
||||
4. Review code thoroughly
|
||||
5. Keep changes focused
|
||||
6. Use conventional commits
|
||||
|
||||
```bash
|
||||
# Good commit messages
|
||||
git commit -m "feat(device): add support for zigbee devices"
|
||||
git commit -m "fix(api): handle timeout errors properly"
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tool Development Guide](tools.md)
|
||||
- [Interface Documentation](interfaces.md)
|
||||
- [Testing Guide](../testing.md)
|
||||
296
docs/development/interfaces.md
Normal file
296
docs/development/interfaces.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Interface Documentation
|
||||
|
||||
This document describes the core interfaces used throughout the Home Assistant MCP.
|
||||
|
||||
## Core Interfaces
|
||||
|
||||
### Tool Interface
|
||||
|
||||
```typescript
|
||||
interface Tool {
|
||||
/** Unique identifier for the tool */
|
||||
id: string;
|
||||
|
||||
/** Human-readable name */
|
||||
name: string;
|
||||
|
||||
/** Detailed description */
|
||||
description: string;
|
||||
|
||||
/** Semantic version */
|
||||
version: string;
|
||||
|
||||
/** Tool category */
|
||||
category: ToolCategory;
|
||||
|
||||
/** Execute tool functionality */
|
||||
execute(params: any): Promise<ToolResult>;
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Result
|
||||
|
||||
```typescript
|
||||
interface ToolResult {
|
||||
/** Operation success status */
|
||||
success: boolean;
|
||||
|
||||
/** Response data */
|
||||
data?: any;
|
||||
|
||||
/** Error message if failed */
|
||||
message?: string;
|
||||
|
||||
/** Error code if failed */
|
||||
error_code?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Tool Category
|
||||
|
||||
```typescript
|
||||
enum ToolCategory {
|
||||
DeviceManagement = 'device_management',
|
||||
HistoryState = 'history_state',
|
||||
Automation = 'automation',
|
||||
AddonsPackages = 'addons_packages',
|
||||
Notifications = 'notifications',
|
||||
Events = 'events',
|
||||
Utility = 'utility'
|
||||
}
|
||||
```
|
||||
|
||||
## Event Interfaces
|
||||
|
||||
### Event Subscription
|
||||
|
||||
```typescript
|
||||
interface EventSubscription {
|
||||
/** Unique subscription ID */
|
||||
id: string;
|
||||
|
||||
/** Event type to subscribe to */
|
||||
event_type: string;
|
||||
|
||||
/** Optional entity ID filter */
|
||||
entity_id?: string;
|
||||
|
||||
/** Optional domain filter */
|
||||
domain?: string;
|
||||
|
||||
/** Subscription creation timestamp */
|
||||
created_at: string;
|
||||
|
||||
/** Last event timestamp */
|
||||
last_event?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Event Message
|
||||
|
||||
```typescript
|
||||
interface EventMessage {
|
||||
/** Event type */
|
||||
event_type: string;
|
||||
|
||||
/** Entity ID if applicable */
|
||||
entity_id?: string;
|
||||
|
||||
/** Event data */
|
||||
data: any;
|
||||
|
||||
/** Event origin */
|
||||
origin: 'LOCAL' | 'REMOTE';
|
||||
|
||||
/** Event timestamp */
|
||||
time_fired: string;
|
||||
|
||||
/** Event context */
|
||||
context: EventContext;
|
||||
}
|
||||
```
|
||||
|
||||
## Device Interfaces
|
||||
|
||||
### Device
|
||||
|
||||
```typescript
|
||||
interface Device {
|
||||
/** Device ID */
|
||||
id: string;
|
||||
|
||||
/** Device name */
|
||||
name: string;
|
||||
|
||||
/** Device domain */
|
||||
domain: string;
|
||||
|
||||
/** Current state */
|
||||
state: string;
|
||||
|
||||
/** Device attributes */
|
||||
attributes: Record<string, any>;
|
||||
|
||||
/** Device capabilities */
|
||||
capabilities: DeviceCapabilities;
|
||||
}
|
||||
```
|
||||
|
||||
### Device Capabilities
|
||||
|
||||
```typescript
|
||||
interface DeviceCapabilities {
|
||||
/** Supported features */
|
||||
features: string[];
|
||||
|
||||
/** Supported commands */
|
||||
commands: string[];
|
||||
|
||||
/** State attributes */
|
||||
attributes: {
|
||||
/** Attribute name */
|
||||
[key: string]: {
|
||||
/** Attribute type */
|
||||
type: 'string' | 'number' | 'boolean' | 'object';
|
||||
/** Attribute description */
|
||||
description: string;
|
||||
/** Optional value constraints */
|
||||
constraints?: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
enum?: any[];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Authentication Interfaces
|
||||
|
||||
### Auth Token
|
||||
|
||||
```typescript
|
||||
interface AuthToken {
|
||||
/** Token value */
|
||||
token: string;
|
||||
|
||||
/** Token type */
|
||||
type: 'bearer' | 'jwt';
|
||||
|
||||
/** Expiration timestamp */
|
||||
expires_at: string;
|
||||
|
||||
/** Token refresh info */
|
||||
refresh?: {
|
||||
token: string;
|
||||
expires_at: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### User
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
/** User ID */
|
||||
id: string;
|
||||
|
||||
/** Username */
|
||||
username: string;
|
||||
|
||||
/** User type */
|
||||
type: 'admin' | 'user' | 'service';
|
||||
|
||||
/** User permissions */
|
||||
permissions: string[];
|
||||
}
|
||||
```
|
||||
|
||||
## Error Interfaces
|
||||
|
||||
### Tool Error
|
||||
|
||||
```typescript
|
||||
interface ToolError extends Error {
|
||||
/** Error code */
|
||||
code: string;
|
||||
|
||||
/** HTTP status code */
|
||||
status: number;
|
||||
|
||||
/** Error details */
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Error
|
||||
|
||||
```typescript
|
||||
interface ValidationError {
|
||||
/** Error path */
|
||||
path: string;
|
||||
|
||||
/** Error message */
|
||||
message: string;
|
||||
|
||||
/** Error code */
|
||||
code: string;
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Interfaces
|
||||
|
||||
### Tool Configuration
|
||||
|
||||
```typescript
|
||||
interface ToolConfig {
|
||||
/** Enable/disable tool */
|
||||
enabled: boolean;
|
||||
|
||||
/** Tool-specific settings */
|
||||
settings: Record<string, any>;
|
||||
|
||||
/** Rate limiting */
|
||||
rate_limit?: {
|
||||
/** Max requests */
|
||||
max: number;
|
||||
/** Time window in seconds */
|
||||
window: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### System Configuration
|
||||
|
||||
```typescript
|
||||
interface SystemConfig {
|
||||
/** System name */
|
||||
name: string;
|
||||
|
||||
/** Environment */
|
||||
environment: 'development' | 'production';
|
||||
|
||||
/** Log level */
|
||||
log_level: 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
/** Tool configurations */
|
||||
tools: Record<string, ToolConfig>;
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Use TypeScript for all interfaces
|
||||
2. Include JSDoc comments
|
||||
3. Use strict typing
|
||||
4. Keep interfaces focused
|
||||
5. Use consistent naming
|
||||
6. Document constraints
|
||||
7. Version interfaces
|
||||
8. Include examples
|
||||
|
||||
## See Also
|
||||
|
||||
- [Tool Development Guide](tools.md)
|
||||
- [Best Practices](best-practices.md)
|
||||
- [Testing Guide](../testing.md)
|
||||
226
docs/development/tools.md
Normal file
226
docs/development/tools.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# Tool Development Guide
|
||||
|
||||
This guide explains how to create new tools for the Home Assistant MCP.
|
||||
|
||||
## Tool Structure
|
||||
|
||||
Each tool should follow this basic structure:
|
||||
|
||||
```typescript
|
||||
interface Tool {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
version: string;
|
||||
category: ToolCategory;
|
||||
execute(params: any): Promise<ToolResult>;
|
||||
}
|
||||
```
|
||||
|
||||
## Creating a New Tool
|
||||
|
||||
1. Create a new file in the appropriate category directory
|
||||
2. Implement the Tool interface
|
||||
3. Add API endpoints
|
||||
4. Add WebSocket handlers
|
||||
5. Add documentation
|
||||
6. Add tests
|
||||
|
||||
### Example Tool Implementation
|
||||
|
||||
```typescript
|
||||
import { Tool, ToolCategory, ToolResult } from '../interfaces';
|
||||
|
||||
export class MyCustomTool implements Tool {
|
||||
id = 'my_custom_tool';
|
||||
name = 'My Custom Tool';
|
||||
description = 'Description of what the tool does';
|
||||
version = '1.0.0';
|
||||
category = ToolCategory.Utility;
|
||||
|
||||
async execute(params: any): Promise<ToolResult> {
|
||||
// Tool implementation
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
// Tool-specific response data
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tool Categories
|
||||
|
||||
- Device Management
|
||||
- History & State
|
||||
- Automation
|
||||
- Add-ons & Packages
|
||||
- Notifications
|
||||
- Events
|
||||
- Utility
|
||||
|
||||
## API Integration
|
||||
|
||||
### REST Endpoint
|
||||
|
||||
```typescript
|
||||
import { Router } from 'express';
|
||||
import { MyCustomTool } from './my-custom-tool';
|
||||
|
||||
const router = Router();
|
||||
const tool = new MyCustomTool();
|
||||
|
||||
router.post('/api/tools/custom', async (req, res) => {
|
||||
try {
|
||||
const result = await tool.execute(req.body);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### WebSocket Handler
|
||||
|
||||
```typescript
|
||||
import { WebSocketServer } from 'ws';
|
||||
import { MyCustomTool } from './my-custom-tool';
|
||||
|
||||
const tool = new MyCustomTool();
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
ws.on('message', async (message) => {
|
||||
const { type, params } = JSON.parse(message);
|
||||
if (type === 'my_custom_tool') {
|
||||
const result = await tool.execute(params);
|
||||
ws.send(JSON.stringify(result));
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
class ToolError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public code: string,
|
||||
public status: number = 500
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'ToolError';
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in tool
|
||||
async execute(params: any): Promise<ToolResult> {
|
||||
try {
|
||||
// Tool implementation
|
||||
} catch (error) {
|
||||
throw new ToolError(
|
||||
'Operation failed',
|
||||
'TOOL_ERROR',
|
||||
500
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```typescript
|
||||
import { MyCustomTool } from './my-custom-tool';
|
||||
|
||||
describe('MyCustomTool', () => {
|
||||
let tool: MyCustomTool;
|
||||
|
||||
beforeEach(() => {
|
||||
tool = new MyCustomTool();
|
||||
});
|
||||
|
||||
it('should execute successfully', async () => {
|
||||
const result = await tool.execute({
|
||||
// Test parameters
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle errors', async () => {
|
||||
// Error test cases
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
1. Create tool documentation in `docs/tools/category/tool-name.md`
|
||||
2. Update `tools/tools.md` with tool reference
|
||||
3. Add tool to navigation in `mkdocs.yml`
|
||||
|
||||
### Documentation Template
|
||||
|
||||
```markdown
|
||||
# Tool Name
|
||||
|
||||
Description of the tool.
|
||||
|
||||
## Features
|
||||
|
||||
- Feature 1
|
||||
- Feature 2
|
||||
|
||||
## Usage
|
||||
|
||||
### REST API
|
||||
|
||||
```typescript
|
||||
// API endpoints
|
||||
```
|
||||
|
||||
### WebSocket
|
||||
|
||||
```typescript
|
||||
// WebSocket usage
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1
|
||||
|
||||
```typescript
|
||||
// Usage example
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
// Response data structure
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Follow consistent naming conventions
|
||||
2. Implement proper error handling
|
||||
3. Add comprehensive documentation
|
||||
4. Write thorough tests
|
||||
5. Use TypeScript for type safety
|
||||
6. Follow SOLID principles
|
||||
7. Implement rate limiting
|
||||
8. Add proper logging
|
||||
|
||||
## See Also
|
||||
|
||||
- [Interface Documentation](interfaces.md)
|
||||
- [Best Practices](best-practices.md)
|
||||
- [Testing Guide](../testing.md)
|
||||
Reference in New Issue
Block a user