Add OpenAI-powered Home Assistant Analysis Tool
- Implemented comprehensive AI-driven system analysis using OpenAI's GPT-4 - Created interactive CLI tool for Home Assistant device and system insights - Added support for standard and custom prompt-based analysis - Integrated MCP server data collection with intelligent AI processing - Updated package.json with new OpenAI and XML dependencies - Enhanced README with detailed OpenAI integration documentation
This commit is contained in:
72
README.md
72
README.md
@@ -59,6 +59,9 @@ See [SSE_API.md](docs/SSE_API.md) for complete documentation of the SSE system.
|
||||
- [Add-on Management](#add-on-management)
|
||||
- [Package Management](#package-management)
|
||||
- [Automation Management](#automation-management)
|
||||
- [OpenAI Integration](#openai-integration)
|
||||
- [Standard Analysis](#1-standard-analysis)
|
||||
- [Custom Prompt Analysis](#2-custom-prompt-analysis)
|
||||
- [Natural Language Integration](#natural-language-integration)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Project Status](#project-status)
|
||||
@@ -667,6 +670,75 @@ async function executeAction() {
|
||||
}
|
||||
```
|
||||
|
||||
## OpenAI Integration
|
||||
|
||||
The server includes powerful AI analysis capabilities powered by OpenAI's GPT-4 model. This feature provides intelligent analysis of your Home Assistant setup through two main modes:
|
||||
|
||||
### 1. Standard Analysis
|
||||
|
||||
Performs a comprehensive system analysis including:
|
||||
- System Overview
|
||||
- Performance Analysis
|
||||
- Security Assessment
|
||||
- Optimization Recommendations
|
||||
- Maintenance Tasks
|
||||
|
||||
```bash
|
||||
# Run standard analysis
|
||||
npm run test:openai
|
||||
# Select option 1 when prompted
|
||||
```
|
||||
|
||||
### 2. Custom Prompt Analysis
|
||||
|
||||
Allows you to ask specific questions about your Home Assistant setup. The analysis can include:
|
||||
- Device States
|
||||
- Configuration Details
|
||||
- Active Devices
|
||||
- Device Attributes (brightness, temperature, etc.)
|
||||
|
||||
```bash
|
||||
# Run custom analysis
|
||||
npm run test:openai
|
||||
# Select option 2 when prompted
|
||||
```
|
||||
|
||||
#### Available Variables
|
||||
When using custom prompts, you can use these variables:
|
||||
- `{device_count}`: Total number of devices
|
||||
- `{device_types}`: List of device types
|
||||
- `{device_states}`: Current states of devices
|
||||
- `{device_examples}`: Example devices and their states
|
||||
|
||||
#### Example Custom Prompts
|
||||
```
|
||||
"Show me all active lights"
|
||||
"Which devices in {device_types} need maintenance?"
|
||||
"Analyze my {device_count} devices and suggest automations"
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
To use the OpenAI integration, you need to set up your OpenAI API key in the `.env` file:
|
||||
```env
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
```
|
||||
|
||||
### Features
|
||||
- 🔍 Intelligent device state analysis
|
||||
- 📊 System health assessment
|
||||
- 🤖 Smart automation suggestions
|
||||
- 🔧 Maintenance recommendations
|
||||
- 💡 Custom query support
|
||||
- 🔄 Real-time device state information
|
||||
|
||||
### Token Usage Optimization
|
||||
The analysis tool includes smart token usage optimization:
|
||||
- Automatic filtering of relevant devices based on query
|
||||
- Fallback to summarized data for large systems
|
||||
- Intelligent attribute selection based on device types
|
||||
- Automatic retry with condensed information if token limit is reached
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
|
||||
580
openai_test.ts
Normal file
580
openai_test.ts
Normal file
@@ -0,0 +1,580 @@
|
||||
import fetch from "node-fetch";
|
||||
import OpenAI from "openai";
|
||||
import { DOMParser, Element, Document } from '@xmldom/xmldom';
|
||||
import dotenv from 'dotenv';
|
||||
import readline from 'readline';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
// Retrieve API keys from environment variables
|
||||
const openaiApiKey = process.env.OPENAI_API_KEY;
|
||||
const hassToken = process.env.HASS_TOKEN;
|
||||
|
||||
if (!openaiApiKey) {
|
||||
console.error("Please set the OPENAI_API_KEY environment variable.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!hassToken) {
|
||||
console.error("Please set the HASS_TOKEN environment variable.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: openaiApiKey,
|
||||
});
|
||||
|
||||
// MCP Server configuration
|
||||
const MCP_SERVER = process.env.MCP_SERVER || 'http://localhost:3000';
|
||||
|
||||
interface McpTool {
|
||||
name: string;
|
||||
description: string;
|
||||
parameters: {
|
||||
properties: Record<string, any>;
|
||||
required: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface ToolsResponse {
|
||||
tools: McpTool[];
|
||||
}
|
||||
|
||||
interface SystemAnalysis {
|
||||
overview: {
|
||||
state: string;
|
||||
health: string;
|
||||
configurations: string[];
|
||||
integrations: string[];
|
||||
issues: string[];
|
||||
};
|
||||
performance: {
|
||||
resource_usage: string[];
|
||||
response_times: string[];
|
||||
optimization_areas: string[];
|
||||
};
|
||||
security: {
|
||||
current_measures: string[];
|
||||
vulnerabilities: string[];
|
||||
recommendations: string[];
|
||||
};
|
||||
optimization: {
|
||||
performance_suggestions: string[];
|
||||
config_optimizations: string[];
|
||||
integration_improvements: string[];
|
||||
automation_opportunities: string[];
|
||||
};
|
||||
maintenance: {
|
||||
required_updates: string[];
|
||||
cleanup_tasks: string[];
|
||||
regular_tasks: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface McpSchema {
|
||||
tools: McpTool[];
|
||||
prompts: any[];
|
||||
resources: {
|
||||
name: string;
|
||||
url: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
interface McpExecuteResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
devices?: Record<string, any[]>;
|
||||
}
|
||||
|
||||
interface ListDevicesResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
devices?: Record<string, any[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a tool on the MCP server
|
||||
*/
|
||||
async function executeMcpTool(toolName: string, parameters: Record<string, any> = {}): Promise<any> {
|
||||
try {
|
||||
const response = await fetch(`${MCP_SERVER}/mcp/execute`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': `Bearer ${hassToken}`,
|
||||
'Content-Type': "application/json",
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tool: toolName,
|
||||
parameters
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
}
|
||||
console.warn(`Failed to execute tool ${toolName}: ${response.status}`);
|
||||
if (response.status === 401) {
|
||||
console.error("Authentication failed. Please check your HASS_TOKEN.");
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.warn(`Error executing tool ${toolName}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects comprehensive information about the Home Assistant instance using MCP tools
|
||||
*/
|
||||
async function collectHomeAssistantInfo(): Promise<any> {
|
||||
const info: Record<string, any> = {};
|
||||
|
||||
// First, get the MCP schema which contains available tools
|
||||
const schemaResponse = await fetch(`${MCP_SERVER}/mcp`, {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!schemaResponse.ok) {
|
||||
console.error(`Failed to fetch MCP schema: ${schemaResponse.status}`);
|
||||
return info;
|
||||
}
|
||||
|
||||
const schema = await schemaResponse.json() as McpSchema;
|
||||
console.log("Available tools:", schema.tools.map(t => t.name));
|
||||
|
||||
// Execute list_devices to get basic device information
|
||||
console.log("Fetching device information...");
|
||||
try {
|
||||
const deviceInfo = await executeMcpTool('list_devices');
|
||||
if (deviceInfo && deviceInfo.success && deviceInfo.devices) {
|
||||
info.devices = deviceInfo.devices;
|
||||
} else {
|
||||
console.warn(`Failed to list devices: ${deviceInfo?.message || 'Unknown error'}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error fetching devices:", error);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the analysis into a nice looking console output
|
||||
*/
|
||||
function formatAnalysis(analysis: SystemAnalysis): string {
|
||||
const formatSection = (items: string[]): string =>
|
||||
items.map(item => ` • ${item}`).join('\n');
|
||||
|
||||
return `
|
||||
=== System Overview ===
|
||||
Current State: ${analysis.overview.state}
|
||||
Health: ${analysis.overview.health}
|
||||
|
||||
Notable Configurations:
|
||||
${formatSection(analysis.overview.configurations)}
|
||||
|
||||
Active Integrations:
|
||||
${formatSection(analysis.overview.integrations)}
|
||||
|
||||
Identified Issues:
|
||||
${formatSection(analysis.overview.issues)}
|
||||
|
||||
=== Performance Analysis ===
|
||||
Resource Usage:
|
||||
${formatSection(analysis.performance.resource_usage)}
|
||||
|
||||
Response Times:
|
||||
${formatSection(analysis.performance.response_times)}
|
||||
|
||||
Areas Needing Optimization:
|
||||
${formatSection(analysis.performance.optimization_areas)}
|
||||
|
||||
=== Security Assessment ===
|
||||
Current Security Measures:
|
||||
${formatSection(analysis.security.current_measures)}
|
||||
|
||||
Potential Vulnerabilities:
|
||||
${formatSection(analysis.security.vulnerabilities)}
|
||||
|
||||
Security Recommendations:
|
||||
${formatSection(analysis.security.recommendations)}
|
||||
|
||||
=== Optimization Recommendations ===
|
||||
Performance Improvements:
|
||||
${formatSection(analysis.optimization.performance_suggestions)}
|
||||
|
||||
Configuration Optimizations:
|
||||
${formatSection(analysis.optimization.config_optimizations)}
|
||||
|
||||
Integration Improvements:
|
||||
${formatSection(analysis.optimization.integration_improvements)}
|
||||
|
||||
Automation Opportunities:
|
||||
${formatSection(analysis.optimization.automation_opportunities)}
|
||||
|
||||
=== Maintenance Tasks ===
|
||||
Required Updates:
|
||||
${formatSection(analysis.maintenance.required_updates)}
|
||||
|
||||
Cleanup Tasks:
|
||||
${formatSection(analysis.maintenance.cleanup_tasks)}
|
||||
|
||||
Regular Maintenance:
|
||||
${formatSection(analysis.maintenance.regular_tasks)}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates analysis and recommendations using the OpenAI API based on the Home Assistant data
|
||||
*/
|
||||
async function generateAnalysis(haInfo: any): Promise<SystemAnalysis> {
|
||||
// Prepare a summarized version of the data to reduce token count
|
||||
const deviceTypes = haInfo.devices ? Object.keys(haInfo.devices) : [];
|
||||
const deviceStates = haInfo.devices ? Object.entries(haInfo.devices).reduce((acc: Record<string, number>, [domain, devices]) => {
|
||||
acc[domain] = (devices as any[]).length;
|
||||
return acc;
|
||||
}, {}) : {};
|
||||
|
||||
const summarizedInfo = {
|
||||
device_summary: {
|
||||
total_count: deviceTypes.reduce((sum, type) => sum + deviceStates[type], 0),
|
||||
types: deviceTypes,
|
||||
by_type: deviceStates,
|
||||
examples: deviceTypes.slice(0, 3).flatMap(type =>
|
||||
(haInfo.devices[type] as any[]).slice(0, 1).map(device => ({
|
||||
type,
|
||||
entity_id: device.entity_id,
|
||||
state: device.state,
|
||||
attributes: device.attributes
|
||||
}))
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
const prompt = `
|
||||
Analyze this Home Assistant device summary and provide a concise analysis in XML format.
|
||||
Focus on key insights and actionable recommendations.
|
||||
|
||||
Device Summary:
|
||||
${JSON.stringify(summarizedInfo, null, 2)}
|
||||
|
||||
Provide your analysis in this XML format:
|
||||
<analysis>
|
||||
<overview>
|
||||
<state>Brief overall state</state>
|
||||
<health>Brief health assessment</health>
|
||||
<configurations>
|
||||
<item>Key configuration insight</item>
|
||||
</configurations>
|
||||
<integrations>
|
||||
<item>Key integration insight</item>
|
||||
</integrations>
|
||||
<issues>
|
||||
<item>Critical issue if any</item>
|
||||
</issues>
|
||||
</overview>
|
||||
<optimization>
|
||||
<performance_suggestions>
|
||||
<item>Key performance tip</item>
|
||||
</performance_suggestions>
|
||||
<automation_opportunities>
|
||||
<item>Key automation suggestion</item>
|
||||
</automation_opportunities>
|
||||
</optimization>
|
||||
<maintenance>
|
||||
<required_updates>
|
||||
<item>Critical update if needed</item>
|
||||
</required_updates>
|
||||
<regular_tasks>
|
||||
<item>Key maintenance task</item>
|
||||
</regular_tasks>
|
||||
</maintenance>
|
||||
</analysis>`;
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-4",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "You are an expert Home Assistant analyst. Provide very concise, actionable insights in the specified XML format."
|
||||
},
|
||||
{ role: "user", content: prompt },
|
||||
],
|
||||
max_tokens: 500,
|
||||
temperature: 0.7,
|
||||
});
|
||||
|
||||
const result = completion.choices[0].message?.content || "";
|
||||
|
||||
// Parse XML response into structured data
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(result, "text/xml");
|
||||
|
||||
const getItems = (path: string): string[] => {
|
||||
const items = Array.from(xmlDoc.getElementsByTagName('item'))
|
||||
.filter(item => {
|
||||
let parent = item.parentNode;
|
||||
let pathParts = path.split('>').map(p => p.trim());
|
||||
for (let i = pathParts.length - 1; i >= 0; i--) {
|
||||
if (!parent || parent.nodeName !== pathParts[i]) return false;
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return items.map(item => (item as unknown as Element).textContent || "");
|
||||
};
|
||||
|
||||
const getText = (path: string): string => {
|
||||
const pathParts = path.split('>').map(p => p.trim());
|
||||
let currentElement: Document | Element = xmlDoc;
|
||||
for (const part of pathParts) {
|
||||
const elements = currentElement.getElementsByTagName(part);
|
||||
if (elements.length === 0) return "";
|
||||
currentElement = elements[0] as Element;
|
||||
}
|
||||
return currentElement.textContent || "";
|
||||
};
|
||||
|
||||
const analysis: SystemAnalysis = {
|
||||
overview: {
|
||||
state: getText("analysis > overview > state"),
|
||||
health: getText("analysis > overview > health"),
|
||||
configurations: getItems("analysis > overview > configurations"),
|
||||
integrations: getItems("analysis > overview > integrations"),
|
||||
issues: getItems("analysis > overview > issues"),
|
||||
},
|
||||
performance: {
|
||||
resource_usage: getItems("analysis > performance > resource_usage"),
|
||||
response_times: getItems("analysis > performance > response_times"),
|
||||
optimization_areas: getItems("analysis > performance > optimization_areas"),
|
||||
},
|
||||
security: {
|
||||
current_measures: getItems("analysis > security > current_measures"),
|
||||
vulnerabilities: getItems("analysis > security > vulnerabilities"),
|
||||
recommendations: getItems("analysis > security > recommendations"),
|
||||
},
|
||||
optimization: {
|
||||
performance_suggestions: getItems("analysis > optimization > performance_suggestions"),
|
||||
config_optimizations: getItems("analysis > optimization > config_optimizations"),
|
||||
integration_improvements: getItems("analysis > optimization > integration_improvements"),
|
||||
automation_opportunities: getItems("analysis > optimization > automation_opportunities"),
|
||||
},
|
||||
maintenance: {
|
||||
required_updates: getItems("analysis > maintenance > required_updates"),
|
||||
cleanup_tasks: getItems("analysis > maintenance > cleanup_tasks"),
|
||||
regular_tasks: getItems("analysis > maintenance > regular_tasks"),
|
||||
},
|
||||
};
|
||||
|
||||
return analysis;
|
||||
} catch (error) {
|
||||
console.error("Error during OpenAI API call:", error);
|
||||
throw new Error("Failed to generate analysis");
|
||||
}
|
||||
}
|
||||
|
||||
async function getUserInput(question: string): Promise<string> {
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
rl.question(question, (answer) => {
|
||||
rl.close();
|
||||
resolve(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function handleCustomPrompt(haInfo: any): Promise<void> {
|
||||
console.log("\nEnter your custom prompt. Available variables:");
|
||||
console.log("- {device_count}: Total number of devices");
|
||||
console.log("- {device_types}: List of device types");
|
||||
console.log("- {device_states}: Current states of devices");
|
||||
console.log("- {device_examples}: Example devices and their states");
|
||||
console.log("\nExample: 'Analyze my {device_count} devices and suggest automations for {device_types}'");
|
||||
|
||||
const customPrompt = await getUserInput("\nEnter your prompt: ");
|
||||
|
||||
// Prepare the data for variable replacement
|
||||
const deviceTypes = haInfo.devices ? Object.keys(haInfo.devices) : [];
|
||||
const deviceStates = haInfo.devices ? Object.entries(haInfo.devices).reduce((acc: Record<string, number>, [domain, devices]) => {
|
||||
acc[domain] = (devices as any[]).length;
|
||||
return acc;
|
||||
}, {}) : {};
|
||||
|
||||
const totalDevices = deviceTypes.reduce((sum, type) => sum + deviceStates[type], 0);
|
||||
|
||||
// Function to filter relevant devices based on the prompt
|
||||
const getRelevantDevices = (prompt: string, devices: any) => {
|
||||
const relevantTypes = deviceTypes.filter(type =>
|
||||
prompt.toLowerCase().includes(type.toLowerCase()) ||
|
||||
type === 'light' && prompt.toLowerCase().includes('lights') ||
|
||||
type === 'switch' && prompt.toLowerCase().includes('switches')
|
||||
);
|
||||
|
||||
if (relevantTypes.length === 0) {
|
||||
// If no specific types mentioned, return a summary of all types
|
||||
return Object.entries(devices).reduce((acc: any, [domain, deviceList]) => {
|
||||
acc[domain] = {
|
||||
count: (deviceList as any[]).length,
|
||||
example: (deviceList as any[])[0]
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
return relevantTypes.reduce((acc: any, type) => {
|
||||
if (devices[type]) {
|
||||
acc[type] = devices[type];
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const relevantDevices = getRelevantDevices(customPrompt, haInfo.devices);
|
||||
|
||||
// Replace variables in the prompt
|
||||
let formattedPrompt = `
|
||||
Here is the current state of your Home Assistant devices:
|
||||
|
||||
Total Devices: ${totalDevices}
|
||||
Device Types: ${deviceTypes.join(', ')}
|
||||
|
||||
Relevant Device Information:
|
||||
${JSON.stringify(relevantDevices, null, 2)}
|
||||
|
||||
User Query: ${customPrompt}
|
||||
|
||||
Please analyze this information and provide a detailed response focusing specifically on what was asked.
|
||||
If the query is about specific device types, please filter and show only relevant information.
|
||||
Include specific entity IDs and states in your response when applicable.
|
||||
`;
|
||||
|
||||
try {
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: "gpt-4",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: `You are an expert Home Assistant analyst with direct access to the current state of a Home Assistant instance.
|
||||
When analyzing device states:
|
||||
- Always mention specific entity IDs when discussing devices
|
||||
- Include current state values and relevant attributes
|
||||
- If discussing lights, mention brightness levels if available
|
||||
- For climate devices, include temperature and mode information
|
||||
- For switches and other binary devices, clearly state if they are on/off
|
||||
- Group related devices together in your analysis
|
||||
- Provide specific, actionable insights based on the current states`
|
||||
},
|
||||
{ role: "user", content: formattedPrompt },
|
||||
],
|
||||
max_tokens: 1000,
|
||||
temperature: 0.3,
|
||||
});
|
||||
|
||||
console.log("\nAnalysis Results:\n");
|
||||
console.log(completion.choices[0].message?.content || "No response generated");
|
||||
} catch (error) {
|
||||
console.error("Error during OpenAI API call:", error);
|
||||
if (error instanceof Error && error.message.includes('maximum context length')) {
|
||||
console.log("\nTrying with more concise data...");
|
||||
// Retry with even more summarized data
|
||||
const summarizedDevices = Object.entries(relevantDevices).reduce((acc: any, [type, devices]) => {
|
||||
if (Array.isArray(devices)) {
|
||||
const activeDevices = devices.filter((d: any) =>
|
||||
d.state === 'on' ||
|
||||
d.state === 'home' ||
|
||||
(typeof d.state === 'number' && d.state > 0)
|
||||
);
|
||||
|
||||
acc[type] = {
|
||||
total: devices.length,
|
||||
active: activeDevices.length,
|
||||
active_devices: activeDevices.map((d: any) => ({
|
||||
entity_id: d.entity_id,
|
||||
state: d.state,
|
||||
name: d.attributes?.friendly_name || d.entity_id,
|
||||
...(d.attributes?.brightness && { brightness: Math.round((d.attributes.brightness / 255) * 100) + '%' }),
|
||||
...(d.attributes?.temperature && { temperature: d.attributes.temperature }),
|
||||
...(d.attributes?.hvac_mode && { mode: d.attributes.hvac_mode })
|
||||
}))
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const retryPrompt = `
|
||||
Analyzing Home Assistant devices:
|
||||
Total Devices: ${totalDevices}
|
||||
Device Types: ${deviceTypes.join(', ')}
|
||||
|
||||
Relevant Device Summary:
|
||||
${JSON.stringify(summarizedDevices, null, 2)}
|
||||
|
||||
User Query: ${customPrompt}
|
||||
|
||||
Please provide a detailed analysis focusing on active devices.
|
||||
Include specific device names, states, and any relevant attributes (brightness, temperature, etc.).
|
||||
Group similar devices together in your response.
|
||||
`;
|
||||
|
||||
try {
|
||||
const retryCompletion = await openai.chat.completions.create({
|
||||
model: "gpt-4",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "You are an expert Home Assistant analyst. Provide concise, focused answers about device states and configurations."
|
||||
},
|
||||
{ role: "user", content: retryPrompt },
|
||||
],
|
||||
max_tokens: 1000,
|
||||
temperature: 0.3,
|
||||
});
|
||||
|
||||
console.log("\nAnalysis Results:\n");
|
||||
console.log(retryCompletion.choices[0].message?.content || "No response generated");
|
||||
} catch (retryError) {
|
||||
console.error("Error during retry:", retryError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Collecting Home Assistant information...");
|
||||
const haInfo = await collectHomeAssistantInfo();
|
||||
if (!Object.keys(haInfo).length) {
|
||||
console.error("Failed to collect any Home Assistant information. Exiting.");
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = await getUserInput(
|
||||
"\nSelect mode:\n1. Standard Analysis\n2. Custom Prompt\nEnter choice (1 or 2): "
|
||||
);
|
||||
|
||||
if (mode === "2") {
|
||||
await handleCustomPrompt(haInfo);
|
||||
} else {
|
||||
console.log("Generating standard analysis and recommendations...");
|
||||
try {
|
||||
const analysis = await generateAnalysis(haInfo);
|
||||
const formattedAnalysis = formatAnalysis(analysis);
|
||||
console.log("\nHome Assistant Analysis and Recommendations:\n");
|
||||
console.log(formattedAnalysis);
|
||||
} catch (error) {
|
||||
console.error("Error generating analysis:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Unexpected error:", error);
|
||||
});
|
||||
@@ -11,6 +11,7 @@
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config=jest.config.cjs",
|
||||
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --config=jest.config.cjs --coverage",
|
||||
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --config=jest.config.cjs --watch",
|
||||
"test:openai": "tsx openai_test.ts",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"lint:fix": "eslint src --ext .ts --fix",
|
||||
"prepare": "npm run build",
|
||||
@@ -21,6 +22,8 @@
|
||||
"dependencies": {
|
||||
"@digital-alchemy/core": "^24.11.4",
|
||||
"@digital-alchemy/hass": "^24.11.4",
|
||||
"@types/xmldom": "^0.1.34",
|
||||
"@xmldom/xmldom": "^0.9.7",
|
||||
"ajv": "^8.12.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
@@ -41,6 +44,8 @@
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/ws": "^8.5.10",
|
||||
"jest": "^29.7.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"openai": "^4.82.0",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-jest": "^29.1.2",
|
||||
"tsx": "^4.7.0",
|
||||
@@ -48,4 +53,4 @@
|
||||
},
|
||||
"author": "Jango Blockchained",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user