ha-mcp-dev 6.3.1.dev137__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ha_mcp/__init__.py +51 -0
- ha_mcp/__main__.py +753 -0
- ha_mcp/_pypi_marker +0 -0
- ha_mcp/auth/__init__.py +10 -0
- ha_mcp/auth/consent_form.py +501 -0
- ha_mcp/auth/provider.py +786 -0
- ha_mcp/client/__init__.py +10 -0
- ha_mcp/client/rest_client.py +924 -0
- ha_mcp/client/websocket_client.py +656 -0
- ha_mcp/client/websocket_listener.py +340 -0
- ha_mcp/config.py +175 -0
- ha_mcp/errors.py +399 -0
- ha_mcp/py.typed +0 -0
- ha_mcp/resources/card_types.json +48 -0
- ha_mcp/resources/dashboard_guide.md +573 -0
- ha_mcp/server.py +189 -0
- ha_mcp/smoke_test.py +108 -0
- ha_mcp/tools/__init__.py +11 -0
- ha_mcp/tools/backup.py +457 -0
- ha_mcp/tools/device_control.py +687 -0
- ha_mcp/tools/enhanced.py +191 -0
- ha_mcp/tools/helpers.py +184 -0
- ha_mcp/tools/registry.py +199 -0
- ha_mcp/tools/smart_search.py +797 -0
- ha_mcp/tools/tools_addons.py +343 -0
- ha_mcp/tools/tools_areas.py +478 -0
- ha_mcp/tools/tools_blueprints.py +279 -0
- ha_mcp/tools/tools_bug_report.py +570 -0
- ha_mcp/tools/tools_calendar.py +391 -0
- ha_mcp/tools/tools_camera.py +149 -0
- ha_mcp/tools/tools_config_automations.py +508 -0
- ha_mcp/tools/tools_config_dashboards.py +1502 -0
- ha_mcp/tools/tools_config_entry_flow.py +223 -0
- ha_mcp/tools/tools_config_helpers.py +925 -0
- ha_mcp/tools/tools_config_info.py +258 -0
- ha_mcp/tools/tools_config_scripts.py +267 -0
- ha_mcp/tools/tools_entities.py +76 -0
- ha_mcp/tools/tools_filesystem.py +589 -0
- ha_mcp/tools/tools_groups.py +306 -0
- ha_mcp/tools/tools_hacs.py +787 -0
- ha_mcp/tools/tools_history.py +714 -0
- ha_mcp/tools/tools_integrations.py +297 -0
- ha_mcp/tools/tools_labels.py +713 -0
- ha_mcp/tools/tools_mcp_component.py +305 -0
- ha_mcp/tools/tools_registry.py +1115 -0
- ha_mcp/tools/tools_resources.py +622 -0
- ha_mcp/tools/tools_search.py +573 -0
- ha_mcp/tools/tools_service.py +211 -0
- ha_mcp/tools/tools_services.py +352 -0
- ha_mcp/tools/tools_system.py +363 -0
- ha_mcp/tools/tools_todo.py +530 -0
- ha_mcp/tools/tools_traces.py +496 -0
- ha_mcp/tools/tools_updates.py +476 -0
- ha_mcp/tools/tools_utility.py +519 -0
- ha_mcp/tools/tools_voice_assistant.py +345 -0
- ha_mcp/tools/tools_zones.py +387 -0
- ha_mcp/tools/util_helpers.py +199 -0
- ha_mcp/utils/__init__.py +30 -0
- ha_mcp/utils/domain_handlers.py +380 -0
- ha_mcp/utils/fuzzy_search.py +339 -0
- ha_mcp/utils/operation_manager.py +442 -0
- ha_mcp/utils/usage_logger.py +290 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/METADATA +229 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/RECORD +71 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/WHEEL +5 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/entry_points.txt +7 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/licenses/LICENSE +21 -0
- ha_mcp_dev-6.3.1.dev137.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/test_constants.py +15 -0
- tests/test_env_manager.py +336 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Usage logging for MCP tool calls to track usage patterns and performance metrics.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from collections import deque
|
|
10
|
+
from dataclasses import asdict, dataclass
|
|
11
|
+
from datetime import UTC, datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from queue import Queue
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Default ring buffer size - keeps last N entries in memory
|
|
18
|
+
DEFAULT_RING_BUFFER_SIZE = 200
|
|
19
|
+
|
|
20
|
+
# Average log entries per tool call (empirically observed)
|
|
21
|
+
# This includes the tool itself plus any associated operations
|
|
22
|
+
AVG_LOG_ENTRIES_PER_TOOL = 3
|
|
23
|
+
|
|
24
|
+
# Startup log collection duration in seconds
|
|
25
|
+
STARTUP_LOG_DURATION_SECONDS = 60
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class StartupLogCollector(logging.Handler):
|
|
29
|
+
"""Collects log messages during the first minute of server startup."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, duration_seconds: int = STARTUP_LOG_DURATION_SECONDS):
|
|
32
|
+
super().__init__()
|
|
33
|
+
self._start_time = time.time()
|
|
34
|
+
self._duration = duration_seconds
|
|
35
|
+
self._logs: list[dict[str, Any]] = []
|
|
36
|
+
self._lock = threading.Lock()
|
|
37
|
+
self._active = True
|
|
38
|
+
|
|
39
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
40
|
+
"""Capture log record if within startup window."""
|
|
41
|
+
if not self._active:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
elapsed = time.time() - self._start_time
|
|
45
|
+
if elapsed > self._duration:
|
|
46
|
+
self._active = False
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
with self._lock:
|
|
50
|
+
self._logs.append({
|
|
51
|
+
"timestamp": datetime.now(UTC).isoformat(),
|
|
52
|
+
"level": record.levelname,
|
|
53
|
+
"logger": record.name,
|
|
54
|
+
"message": record.getMessage(),
|
|
55
|
+
"elapsed_seconds": round(elapsed, 2),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
def get_logs(self) -> list[dict[str, Any]]:
|
|
59
|
+
"""Get collected startup logs."""
|
|
60
|
+
with self._lock:
|
|
61
|
+
return list(self._logs)
|
|
62
|
+
|
|
63
|
+
def is_active(self) -> bool:
|
|
64
|
+
"""Check if still collecting startup logs."""
|
|
65
|
+
return self._active and (time.time() - self._start_time) <= self._duration
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Global startup log collector - initialized at module import
|
|
69
|
+
_startup_collector: StartupLogCollector | None = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _init_startup_collector() -> None:
|
|
73
|
+
"""Initialize startup log collector and attach to root logger."""
|
|
74
|
+
global _startup_collector
|
|
75
|
+
if _startup_collector is None:
|
|
76
|
+
_startup_collector = StartupLogCollector()
|
|
77
|
+
_startup_collector.setLevel(logging.DEBUG)
|
|
78
|
+
logging.getLogger().addHandler(_startup_collector)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Initialize on module load
|
|
82
|
+
_init_startup_collector()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_startup_logs() -> list[dict[str, Any]]:
|
|
86
|
+
"""Get startup logs collected during the first minute."""
|
|
87
|
+
if _startup_collector is None:
|
|
88
|
+
return []
|
|
89
|
+
return _startup_collector.get_logs()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class ToolUsageLog:
|
|
94
|
+
"""Single tool usage log entry."""
|
|
95
|
+
|
|
96
|
+
timestamp: str
|
|
97
|
+
tool_name: str
|
|
98
|
+
parameters: dict[str, Any]
|
|
99
|
+
execution_time_ms: float
|
|
100
|
+
success: bool
|
|
101
|
+
error_message: str | None = None
|
|
102
|
+
response_size_bytes: int | None = None
|
|
103
|
+
user_context: str | None = None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class UsageLogger:
|
|
107
|
+
"""Async disk logger for MCP tool usage tracking with in-memory ring buffer."""
|
|
108
|
+
|
|
109
|
+
def __init__(
|
|
110
|
+
self,
|
|
111
|
+
log_file_path: str | None = None,
|
|
112
|
+
ring_buffer_size: int = DEFAULT_RING_BUFFER_SIZE,
|
|
113
|
+
):
|
|
114
|
+
self._enabled = True
|
|
115
|
+
|
|
116
|
+
if log_file_path:
|
|
117
|
+
self.log_file_path = Path(log_file_path)
|
|
118
|
+
else:
|
|
119
|
+
# Use user's home directory by default to avoid read-only filesystem errors
|
|
120
|
+
# when running via uvx/npx which might have read-only CWD
|
|
121
|
+
self.log_file_path = Path.home() / ".ha-mcp" / "logs" / "mcp_usage.jsonl"
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
self.log_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
125
|
+
except OSError:
|
|
126
|
+
# Directory creation failed (e.g., read-only filesystem)
|
|
127
|
+
# Disable logging silently to avoid disrupting the MCP server
|
|
128
|
+
self._enabled = False
|
|
129
|
+
|
|
130
|
+
# In-memory ring buffer for fast access to recent logs
|
|
131
|
+
# Thread-safe: deque is thread-safe for append/pop operations
|
|
132
|
+
self._ring_buffer: deque[dict[str, Any]] = deque(maxlen=ring_buffer_size)
|
|
133
|
+
self._buffer_lock = threading.Lock()
|
|
134
|
+
|
|
135
|
+
# Thread-safe queue for disk writes
|
|
136
|
+
self._log_queue: Queue[ToolUsageLog] = Queue()
|
|
137
|
+
self._logger_thread: threading.Thread | None = None
|
|
138
|
+
self._stop_event = threading.Event()
|
|
139
|
+
if self._enabled:
|
|
140
|
+
self._start_logger_thread()
|
|
141
|
+
|
|
142
|
+
def _start_logger_thread(self) -> None:
|
|
143
|
+
"""Start background thread for disk writes."""
|
|
144
|
+
self._logger_thread = threading.Thread(
|
|
145
|
+
target=self._log_writer_worker, daemon=True
|
|
146
|
+
)
|
|
147
|
+
self._logger_thread.start()
|
|
148
|
+
|
|
149
|
+
def _log_writer_worker(self) -> None:
|
|
150
|
+
"""Background thread worker for writing logs to disk."""
|
|
151
|
+
while not self._stop_event.is_set():
|
|
152
|
+
try:
|
|
153
|
+
# Wait for log entry with timeout to check stop event
|
|
154
|
+
if not self._log_queue.empty():
|
|
155
|
+
log_entry = self._log_queue.get(timeout=1.0)
|
|
156
|
+
self._write_log_entry(log_entry)
|
|
157
|
+
else:
|
|
158
|
+
# Sleep briefly to avoid busy waiting
|
|
159
|
+
threading.Event().wait(0.1)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
# Silent error handling to avoid disrupting MCP server
|
|
162
|
+
print(f"Usage logger error: {e}")
|
|
163
|
+
|
|
164
|
+
def _write_log_entry(self, log_entry: ToolUsageLog) -> None:
|
|
165
|
+
"""Write single log entry to disk."""
|
|
166
|
+
try:
|
|
167
|
+
with open(self.log_file_path, "a", encoding="utf-8") as f:
|
|
168
|
+
json.dump(asdict(log_entry), f, ensure_ascii=False)
|
|
169
|
+
f.write("\n")
|
|
170
|
+
except Exception:
|
|
171
|
+
# Silent error handling
|
|
172
|
+
pass
|
|
173
|
+
|
|
174
|
+
def log_tool_usage(
|
|
175
|
+
self,
|
|
176
|
+
tool_name: str,
|
|
177
|
+
parameters: dict[str, Any],
|
|
178
|
+
execution_time_ms: float,
|
|
179
|
+
success: bool,
|
|
180
|
+
error_message: str | None = None,
|
|
181
|
+
response_size_bytes: int | None = None,
|
|
182
|
+
user_context: str | None = None,
|
|
183
|
+
) -> None:
|
|
184
|
+
"""Log tool usage (non-blocking)."""
|
|
185
|
+
if not self._enabled:
|
|
186
|
+
return
|
|
187
|
+
try:
|
|
188
|
+
log_entry = ToolUsageLog(
|
|
189
|
+
timestamp=datetime.now(UTC).isoformat(),
|
|
190
|
+
tool_name=tool_name,
|
|
191
|
+
parameters=parameters,
|
|
192
|
+
execution_time_ms=execution_time_ms,
|
|
193
|
+
success=success,
|
|
194
|
+
error_message=error_message,
|
|
195
|
+
response_size_bytes=response_size_bytes,
|
|
196
|
+
user_context=user_context,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Add to ring buffer (thread-safe)
|
|
200
|
+
entry_dict = asdict(log_entry)
|
|
201
|
+
with self._buffer_lock:
|
|
202
|
+
self._ring_buffer.append(entry_dict)
|
|
203
|
+
|
|
204
|
+
# Queue for disk write
|
|
205
|
+
self._log_queue.put(log_entry)
|
|
206
|
+
except Exception:
|
|
207
|
+
# Silent error handling to never break MCP server
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
def get_recent_entries(self, count: int) -> list[dict[str, Any]]:
|
|
211
|
+
"""
|
|
212
|
+
Get recent log entries from the in-memory ring buffer.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
count: Maximum number of entries to return
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
List of log entries as dictionaries, ordered newest to oldest
|
|
219
|
+
"""
|
|
220
|
+
with self._buffer_lock:
|
|
221
|
+
# Get the last N entries, reversed to newest-first order
|
|
222
|
+
buffer_list = list(self._ring_buffer)
|
|
223
|
+
return list(reversed(buffer_list[-count:]))
|
|
224
|
+
|
|
225
|
+
def shutdown(self) -> None:
|
|
226
|
+
"""Gracefully shutdown logger."""
|
|
227
|
+
self._stop_event.set()
|
|
228
|
+
if self._logger_thread and self._logger_thread.is_alive():
|
|
229
|
+
self._logger_thread.join(timeout=2.0)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Global logger instance
|
|
233
|
+
_usage_logger: UsageLogger | None = None
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def get_usage_logger() -> UsageLogger:
|
|
237
|
+
"""Get global usage logger instance."""
|
|
238
|
+
global _usage_logger
|
|
239
|
+
if _usage_logger is None:
|
|
240
|
+
_usage_logger = UsageLogger()
|
|
241
|
+
return _usage_logger
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def log_tool_call(
|
|
245
|
+
tool_name: str,
|
|
246
|
+
parameters: dict[str, Any],
|
|
247
|
+
execution_time_ms: float,
|
|
248
|
+
success: bool,
|
|
249
|
+
error_message: str | None = None,
|
|
250
|
+
response_size_bytes: int | None = None,
|
|
251
|
+
user_context: str | None = None,
|
|
252
|
+
) -> None:
|
|
253
|
+
"""Convenience function to log tool usage."""
|
|
254
|
+
logger = get_usage_logger()
|
|
255
|
+
logger.log_tool_usage(
|
|
256
|
+
tool_name=tool_name,
|
|
257
|
+
parameters=parameters,
|
|
258
|
+
execution_time_ms=execution_time_ms,
|
|
259
|
+
success=success,
|
|
260
|
+
error_message=error_message,
|
|
261
|
+
response_size_bytes=response_size_bytes,
|
|
262
|
+
user_context=user_context,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def shutdown_usage_logger() -> None:
|
|
267
|
+
"""Shutdown global usage logger."""
|
|
268
|
+
global _usage_logger
|
|
269
|
+
if _usage_logger:
|
|
270
|
+
_usage_logger.shutdown()
|
|
271
|
+
_usage_logger = None
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_recent_logs(max_entries: int = 20) -> list[dict[str, Any]]:
|
|
275
|
+
"""
|
|
276
|
+
Get recent log entries from the in-memory ring buffer.
|
|
277
|
+
|
|
278
|
+
This is O(1) access - no file I/O required.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
max_entries: Maximum number of log entries to return (most recent first)
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List of log entries as dictionaries, ordered newest to oldest
|
|
285
|
+
"""
|
|
286
|
+
logger = get_usage_logger()
|
|
287
|
+
if not logger._enabled:
|
|
288
|
+
return []
|
|
289
|
+
|
|
290
|
+
return logger.get_recent_entries(max_entries)
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ha-mcp-dev
|
|
3
|
+
Version: 6.3.1.dev137
|
|
4
|
+
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
|
+
Author-email: Julien <github@qc-h.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/homeassistant-ai/ha-mcp
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/homeassistant-ai/ha-mcp/issues
|
|
9
|
+
Project-URL: Repository, https://github.com/homeassistant-ai/ha-mcp
|
|
10
|
+
Keywords: mcp,home-assistant,ai,automation,smart-home
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Home Automation
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: <3.14,>=3.13
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: fastmcp>=2.11.0
|
|
22
|
+
Requires-Dist: httpx[socks]<1.0,>=0.27.0
|
|
23
|
+
Requires-Dist: jq>=1.8.0; sys_platform != "win32"
|
|
24
|
+
Requires-Dist: pydantic>=2.5.0
|
|
25
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
26
|
+
Requires-Dist: truststore>=0.10.0
|
|
27
|
+
Requires-Dist: websockets>=12.0
|
|
28
|
+
Requires-Dist: cryptography>=45.0.7
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
<img src="docs/img/ha-mcp-logo.png" alt="Home Assistant MCP Server Logo" width="300"/>
|
|
33
|
+
|
|
34
|
+
# The Unofficial and Awesome Home Assistant MCP Server
|
|
35
|
+
|
|
36
|
+
<!-- mcp-name: io.github.homeassistant-ai/ha-mcp -->
|
|
37
|
+
|
|
38
|
+
<p align="center">
|
|
39
|
+
<img src="https://img.shields.io/badge/tools-95+-blue" alt="95+ Tools">
|
|
40
|
+
<a href="https://github.com/homeassistant-ai/ha-mcp/releases"><img src="https://img.shields.io/github/v/release/homeassistant-ai/ha-mcp" alt="Release"></a>
|
|
41
|
+
<a href="https://github.com/homeassistant-ai/ha-mcp/actions/workflows/e2e-tests.yml"><img src="https://img.shields.io/github/actions/workflow/status/homeassistant-ai/ha-mcp/e2e-tests.yml?branch=master&label=E2E%20Tests" alt="E2E Tests"></a>
|
|
42
|
+
<a href="LICENSE.md"><img src="https://img.shields.io/github/license/homeassistant-ai/ha-mcp.svg" alt="License"></a>
|
|
43
|
+
<br>
|
|
44
|
+
<a href="https://github.com/homeassistant-ai/ha-mcp/commits/master"><img src="https://img.shields.io/github/commit-activity/m/homeassistant-ai/ha-mcp.svg" alt="Activity"></a>
|
|
45
|
+
<a href="https://github.com/jlowin/fastmcp"><img src="https://img.shields.io/badge/Built%20with-FastMCP-purple" alt="Built with FastMCP"></a>
|
|
46
|
+
<img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fhomeassistant-ai%2Fha-mcp%2Fmaster%2Fpyproject.toml" alt="Python Version">
|
|
47
|
+
<a href="https://github.com/sponsors/julienld"><img src="https://img.shields.io/badge/GitHub_Sponsors-☕-blueviolet" alt="GitHub Sponsors"></a>
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
<p align="center">
|
|
51
|
+
<em>A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with Home Assistant.<br>
|
|
52
|
+
Using natural language, control smart home devices, query states, execute services and manage your automations.</em>
|
|
53
|
+
</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🚀 Get Started
|
|
63
|
+
|
|
64
|
+
### Full guide to get you started with Claude Desktop (~10 min)
|
|
65
|
+
|
|
66
|
+
*No paid subscription required.* Click on your operating system:
|
|
67
|
+
|
|
68
|
+
<p>
|
|
69
|
+
<a href="https://homeassistant-ai.github.io/ha-mcp/guide-macos/"><img src="https://img.shields.io/badge/Setup_Guide_for_macOS-000000?style=for-the-badge&logo=apple&logoColor=white" alt="Setup Guide for macOS" height="120"></a> <a href="https://homeassistant-ai.github.io/ha-mcp/guide-windows/"><img src="https://img.shields.io/badge/Setup_Guide_for_Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white" alt="Setup Guide for Windows" height="120"></a>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
### Quick install (~5 min)
|
|
73
|
+
|
|
74
|
+
<details>
|
|
75
|
+
<summary><b>🍎 macOS</b></summary>
|
|
76
|
+
|
|
77
|
+
1. Go to [claude.ai](https://claude.ai) and sign in (or create a free account)
|
|
78
|
+
2. Open **Terminal** and run:
|
|
79
|
+
```sh
|
|
80
|
+
curl -LsSf https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-macos.sh | sh
|
|
81
|
+
```
|
|
82
|
+
3. [Download Claude Desktop](https://claude.ai/download) (or restart: Claude menu → Quit)
|
|
83
|
+
4. Ask Claude: **"Can you see my Home Assistant?"**
|
|
84
|
+
|
|
85
|
+
You're now connected to the demo environment! [Connect your own Home Assistant →](https://homeassistant-ai.github.io/ha-mcp/guide-macos/#step-6-connect-your-home-assistant)
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
<details>
|
|
90
|
+
<summary><b>🪟 Windows</b></summary>
|
|
91
|
+
|
|
92
|
+
1. Go to [claude.ai](https://claude.ai) and sign in (or create a free account)
|
|
93
|
+
2. Open **Windows PowerShell** (from Start menu) and run:
|
|
94
|
+
```powershell
|
|
95
|
+
irm https://raw.githubusercontent.com/homeassistant-ai/ha-mcp/master/scripts/install-windows.ps1 | iex
|
|
96
|
+
```
|
|
97
|
+
3. [Download Claude Desktop](https://claude.ai/download) (or restart: File → Exit)
|
|
98
|
+
4. Ask Claude: **"Can you see my Home Assistant?"**
|
|
99
|
+
|
|
100
|
+
You're now connected to the demo environment! [Connect your own Home Assistant →](https://homeassistant-ai.github.io/ha-mcp/guide-windows/#step-6-connect-your-home-assistant)
|
|
101
|
+
|
|
102
|
+
</details>
|
|
103
|
+
|
|
104
|
+
### 🧙 Setup Wizard for 15+ clients
|
|
105
|
+
|
|
106
|
+
**Claude Code, Gemini CLI, ChatGPT, Open WebUI, VSCode, Cursor, and more.**
|
|
107
|
+
|
|
108
|
+
<p>
|
|
109
|
+
<a href="https://homeassistant-ai.github.io/ha-mcp/setup/"><img src="https://img.shields.io/badge/Open_Setup_Wizard-4A90D9?style=for-the-badge" alt="Open Setup Wizard" height="40"></a>
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
Having issues? Check the **[FAQ & Troubleshooting](https://homeassistant-ai.github.io/ha-mcp/faq/)**
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 💬 What Can You Do With It?
|
|
117
|
+
|
|
118
|
+
Just talk to Claude naturally. Here are some real examples:
|
|
119
|
+
|
|
120
|
+
| You Say | What Happens |
|
|
121
|
+
|---------|--------------|
|
|
122
|
+
| *"Create an automation that turns on the porch light at sunset"* | Creates the automation with proper triggers and actions |
|
|
123
|
+
| *"Add a weather card to my dashboard"* | Updates your Lovelace dashboard with the new card |
|
|
124
|
+
| *"The motion sensor automation isn't working, debug it"* | Analyzes execution traces, identifies the issue, suggests fixes |
|
|
125
|
+
| *"Make my morning routine automation also turn on the coffee maker"* | Reads the existing automation, adds the new action, updates it |
|
|
126
|
+
| *"Create a script that sets movie mode: dim lights, close blinds, turn on TV"* | Creates a reusable script with the sequence of actions |
|
|
127
|
+
|
|
128
|
+
Spend less time configuring, more time enjoying your smart home.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## ✨ Features
|
|
133
|
+
|
|
134
|
+
| Category | Capabilities |
|
|
135
|
+
|----------|--------------|
|
|
136
|
+
| **🔍 Search** | Fuzzy entity search, deep config search, system overview |
|
|
137
|
+
| **🏠 Control** | Any service, bulk device control, real-time states |
|
|
138
|
+
| **🔧 Manage** | Automations, scripts, helpers, dashboards, areas, zones, groups, calendars, blueprints |
|
|
139
|
+
| **📊 Monitor** | History, statistics, camera snapshots, automation traces, ZHA devices |
|
|
140
|
+
| **💾 System** | Backup/restore, updates, add-ons, device registry |
|
|
141
|
+
| **🔐 Auth** | OAuth 2.1 with DCR for secure multi-user access (Claude.ai), token-based for local clients |
|
|
142
|
+
|
|
143
|
+
<details>
|
|
144
|
+
<summary><b>🛠️ Complete Tool List (97 tools)</b></summary>
|
|
145
|
+
|
|
146
|
+
| Category | Tools |
|
|
147
|
+
|----------|-------|
|
|
148
|
+
| **Search & Discovery** | `ha_search_entities`, `ha_deep_search`, `ha_get_overview`, `ha_get_state` |
|
|
149
|
+
| **Service & Device Control** | `ha_call_service`, `ha_bulk_control`, `ha_get_operation_status`, `ha_get_bulk_status`, `ha_list_services` |
|
|
150
|
+
| **Automations** | `ha_config_get_automation`, `ha_config_set_automation`, `ha_config_remove_automation` |
|
|
151
|
+
| **Scripts** | `ha_config_get_script`, `ha_config_set_script`, `ha_config_remove_script` |
|
|
152
|
+
| **Helper Entities** | `ha_config_list_helpers`, `ha_config_set_helper`, `ha_config_remove_helper` |
|
|
153
|
+
| **Dashboards** | `ha_config_get_dashboard`, `ha_config_set_dashboard`, `ha_config_update_dashboard_metadata`, `ha_config_delete_dashboard`, `ha_get_dashboard_guide`, `ha_get_card_types`, `ha_get_card_documentation` |
|
|
154
|
+
| **Areas & Floors** | `ha_config_list_areas`, `ha_config_set_area`, `ha_config_remove_area`, `ha_config_list_floors`, `ha_config_set_floor`, `ha_config_remove_floor` |
|
|
155
|
+
| **Labels** | `ha_config_get_label`, `ha_config_set_label`, `ha_config_remove_label`, `ha_manage_entity_labels` |
|
|
156
|
+
| **Zones** | `ha_get_zone`, `ha_create_zone`, `ha_update_zone`, `ha_delete_zone` |
|
|
157
|
+
| **Groups** | `ha_config_list_groups`, `ha_config_set_group`, `ha_config_remove_group` |
|
|
158
|
+
| **Todo Lists** | `ha_get_todo`, `ha_add_todo_item`, `ha_update_todo_item`, `ha_remove_todo_item` |
|
|
159
|
+
| **Calendar** | `ha_config_get_calendar_events`, `ha_config_set_calendar_event`, `ha_config_remove_calendar_event` |
|
|
160
|
+
| **Blueprints** | `ha_list_blueprints`, `ha_get_blueprint`, `ha_import_blueprint` |
|
|
161
|
+
| **Device Registry** | `ha_get_device`, `ha_update_device`, `ha_remove_device`, `ha_rename_entity` |
|
|
162
|
+
| **ZHA & Integrations** | `ha_get_zha_devices`, `ha_get_entity_integration_source` |
|
|
163
|
+
| **Add-ons** | `ha_get_addon` |
|
|
164
|
+
| **Camera** | `ha_get_camera_image` |
|
|
165
|
+
| **History & Statistics** | `ha_get_history`, `ha_get_statistics` |
|
|
166
|
+
| **Automation Traces** | `ha_get_automation_traces` |
|
|
167
|
+
| **System & Updates** | `ha_check_config`, `ha_restart`, `ha_reload_core`, `ha_get_system_info`, `ha_get_system_health`, `ha_get_updates` |
|
|
168
|
+
| **Backup & Restore** | `ha_backup_create`, `ha_backup_restore` |
|
|
169
|
+
| **Utility** | `ha_get_logbook`, `ha_eval_template`, `ha_get_domain_docs`, `ha_get_integration` |
|
|
170
|
+
|
|
171
|
+
</details>
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🤝 Contributing
|
|
176
|
+
|
|
177
|
+
For development setup, testing instructions, and contribution guidelines, see **[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
|
178
|
+
|
|
179
|
+
For comprehensive testing documentation, see **[tests/README.md](tests/README.md)**.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🔒 Privacy
|
|
184
|
+
|
|
185
|
+
Ha-mcp runs **locally** on your machine. Your smart home data stays on your network.
|
|
186
|
+
|
|
187
|
+
- **Configurable telemetry** — optional anonymous usage stats
|
|
188
|
+
- **No personal data collection** — we never collect entity names, configs, or device data
|
|
189
|
+
- **User-controlled bug reports** — only sent with your explicit approval
|
|
190
|
+
|
|
191
|
+
For full details, see our [Privacy Policy](PRIVACY.md).
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 📄 License
|
|
196
|
+
|
|
197
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 🙏 Acknowledgments
|
|
202
|
+
|
|
203
|
+
- **[Home Assistant](https://home-assistant.io/)**: Amazing smart home platform (!)
|
|
204
|
+
- **[FastMCP](https://github.com/jlowin/fastmcp)**: Excellent MCP server framework
|
|
205
|
+
- **[Model Context Protocol](https://modelcontextprotocol.io/)**: Standardized AI-application communication
|
|
206
|
+
- **[Claude Code](https://github.com/anthropics/claude-code)**: AI-powered coding assistant
|
|
207
|
+
|
|
208
|
+
## 👥 Contributors
|
|
209
|
+
|
|
210
|
+
- **[@julienld](https://github.com/julienld)** — Project maintainer & core contributor.
|
|
211
|
+
- **[@kingbear2](https://github.com/kingbear2)** — Windows UV setup guide.
|
|
212
|
+
- **[@sergeykad](https://github.com/sergeykad)** — Dashboard card-level CRUD operations, better changelogs and removed the dependency to textdistance/numpy.
|
|
213
|
+
- **[@konradwalsh](https://github.com/konradwalsh)** — Financial support via [GitHub Sponsors](https://github.com/sponsors/julienld). Thank you! ☕
|
|
214
|
+
- **[@cj-elevate](https://github.com/cj-elevate)** — Integration & entity management tools (enable/disable/delete).
|
|
215
|
+
- **[@kingpanther13](https://github.com/kingpanther13)** — Native solutions guidance to reduce template overuse.
|
|
216
|
+
- **[@Raygooo](https://github.com/Raygooo)** — SOCKS proxy support.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 💬 Community
|
|
221
|
+
|
|
222
|
+
- **[GitHub Discussions](https://github.com/homeassistant-ai/ha-mcp/discussions)** — Ask questions, share ideas
|
|
223
|
+
- **[Issue Tracker](https://github.com/homeassistant-ai/ha-mcp/issues)** — Report bugs, request features, or suggest tool behavior improvements
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## ⭐ Star History
|
|
228
|
+
|
|
229
|
+
[](https://star-history.com/#homeassistant-ai/ha-mcp&Date)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
ha_mcp/__init__.py,sha256=WnpUJgSF99nlyZeI5nppDjYZ7RqtjAERLpGHiFWOcHQ,1308
|
|
2
|
+
ha_mcp/__main__.py,sha256=sUP5bRI2g1QjKojmz0UhTB5kpAROBMgYhUsg9MspI7k,24583
|
|
3
|
+
ha_mcp/_pypi_marker,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
ha_mcp/config.py,sha256=djlKf9ozL-xC1TuzzkWN4ECbXytkRxZhR3s2CYMuEjI,6427
|
|
5
|
+
ha_mcp/errors.py,sha256=6st0Kud3iACAHaKn-eEb8O5o8Ee9ZcSxpeeDJBRM844,13568
|
|
6
|
+
ha_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
ha_mcp/server.py,sha256=FkioX-XOAkOyE1AfsBuVnA51kkYH9-N12HLuHXBMxso,7163
|
|
8
|
+
ha_mcp/smoke_test.py,sha256=2aABoHVaxNRIFaLKHZdtLr_d1yCYtB6ZUVAU2AWUlIM,3804
|
|
9
|
+
ha_mcp/auth/__init__.py,sha256=4LY5fPgwJvbWFPc9AndpCkvg8PqssN2WCmlQWqLbJgI,304
|
|
10
|
+
ha_mcp/auth/consent_form.py,sha256=SBuhr7A87T2Npkk1BMlmnoSrzgETDDLgJ8i0godUiYs,14708
|
|
11
|
+
ha_mcp/auth/provider.py,sha256=5s55oo7iQjnpPuhgTt-meapLwPIC4r1v_5wrUQ15y8o,30949
|
|
12
|
+
ha_mcp/client/__init__.py,sha256=tPxyY6vQu5up4kwJgqc-ChpjPEBK5QouUedMQSNJdUQ,277
|
|
13
|
+
ha_mcp/client/rest_client.py,sha256=DC_BPnxLbl5UJ-eUo7bXh4oK1b-IqfpA695z6SS8m9E,34505
|
|
14
|
+
ha_mcp/client/websocket_client.py,sha256=Qygz6D79ZS7bJzk6u-DP1Gh7Z_ozfhOzDu-jz-Pglss,24112
|
|
15
|
+
ha_mcp/client/websocket_listener.py,sha256=5LpBqMlbTqaW3oX61t6OAfwbKy4DurKNeZPPzKIMPiU,12587
|
|
16
|
+
ha_mcp/resources/card_types.json,sha256=dRcKOqMpW6zIh2Q2MLNi8woK9gV_aY1YB5Ot9EzUu_M,977
|
|
17
|
+
ha_mcp/resources/dashboard_guide.md,sha256=uJPV5_dmmoS8YFJX98RA_gflUPFyvVkjU-rDJcqQOkw,14967
|
|
18
|
+
ha_mcp/tools/__init__.py,sha256=7aPElrhC2sUXIgCExm3hIlgSYYdi6KyvmfhtOzPFQHM,334
|
|
19
|
+
ha_mcp/tools/backup.py,sha256=BB1BGWPVneebM7Rf7uq0V9hEeKqmlibBrZSavdNy_Xk,17626
|
|
20
|
+
ha_mcp/tools/device_control.py,sha256=QQkx3CENpfNQIf1UA681Bq4GClMx2pJDZR6ZWbX_mGc,27484
|
|
21
|
+
ha_mcp/tools/enhanced.py,sha256=WJgpSgAJOEhuzPOERiJtasPpGE28wgoJ3vvurgP_Wrw,6681
|
|
22
|
+
ha_mcp/tools/helpers.py,sha256=0Z5jGxNVpm6TnwKO7ZO5zOmG-ankhAXprt_xgcfLE1g,5837
|
|
23
|
+
ha_mcp/tools/registry.py,sha256=mTrju-6gbmhlX-nOYOMpwqiN7zdOCQetnBihHBVq50E,7721
|
|
24
|
+
ha_mcp/tools/smart_search.py,sha256=iMtHjCcL9GfDgD0S4KN3I33b1MXHQXh00W05TmTQU2A,34454
|
|
25
|
+
ha_mcp/tools/tools_addons.py,sha256=xEvYfE_tGovvaczIA5O8uugrnsNr6gDFSSW1jNmNfXg,12369
|
|
26
|
+
ha_mcp/tools/tools_areas.py,sha256=aVdAAwqTPk34ZCP6rhxIo_cw01Cj4Dwa2F06gmvIk9A,17792
|
|
27
|
+
ha_mcp/tools/tools_blueprints.py,sha256=ZH4JHheknfhLzSR104Uk4Ulx7wVHFx8IAxJPQdTlIt0,10911
|
|
28
|
+
ha_mcp/tools/tools_bug_report.py,sha256=2sBSPemnSk6hwSvEZnrLkTLIrlGVn7uTIRT-YZhK8f0,18784
|
|
29
|
+
ha_mcp/tools/tools_calendar.py,sha256=Okt6FdBe7oQP6uXggfR8yET-YjczY9-fn9xuIEnPXq8,14767
|
|
30
|
+
ha_mcp/tools/tools_camera.py,sha256=tDrFcoX9tDfLdwCWTD5l7mErYVNBoAy7j_LsMctpspI,5769
|
|
31
|
+
ha_mcp/tools/tools_config_automations.py,sha256=DCr28CK1bcJDu28ButGZAaapzsmA7oGL5N-bzUXeTQU,20786
|
|
32
|
+
ha_mcp/tools/tools_config_dashboards.py,sha256=qRReRlje64l6GGeO6mYCJ7LD8qRdemLavJuzLWyfMMA,60607
|
|
33
|
+
ha_mcp/tools/tools_config_entry_flow.py,sha256=G21DCsD1eDRN9uq3bnDicc8opcpd5gRSxITpKklsR60,7839
|
|
34
|
+
ha_mcp/tools/tools_config_helpers.py,sha256=Qd6iEQx1EmihbO3abMuyfw0Jo-jCNmLuhqNVFmWdTlQ,37694
|
|
35
|
+
ha_mcp/tools/tools_config_info.py,sha256=79TBsFijVQQuiAG6FXeJyMEGkKQxehtOCdKpxUzEtJU,10762
|
|
36
|
+
ha_mcp/tools/tools_config_scripts.py,sha256=vp5laEBb0m73DSlH4vwT7sOFlEqYs_xX_Uy7hC3bQe0,10017
|
|
37
|
+
ha_mcp/tools/tools_entities.py,sha256=uFLx7xgtBnD72MDyKUp24l_5HCvu-ZQ84lWkUglid90,2623
|
|
38
|
+
ha_mcp/tools/tools_filesystem.py,sha256=DXVwIi6x_SnfrR0ZvdlwWIWw-uOttd_wAm5idiMdi-8,20102
|
|
39
|
+
ha_mcp/tools/tools_groups.py,sha256=KpDpUpIlqn7-ZfvdhQKWPrQ3-q7bSq5_MABZjVejjqU,11786
|
|
40
|
+
ha_mcp/tools/tools_hacs.py,sha256=gWTO-SRi7CPx1MCj68Kw94EJO9r1EVduEdwMdnoXfDw,34988
|
|
41
|
+
ha_mcp/tools/tools_history.py,sha256=CgUCi2_T3F536mpx03OgnHD0pCtXtpDe5CnU_VGNLvA,28704
|
|
42
|
+
ha_mcp/tools/tools_integrations.py,sha256=6PH_gQAkvPkGvW4tdd0TU_6qaZVBfnKWjPEVFUeo7uY,11645
|
|
43
|
+
ha_mcp/tools/tools_labels.py,sha256=nJm2bX5i9OGYMLIpSjeBaEWIhOolbgc1Y5L66OIYL08,27150
|
|
44
|
+
ha_mcp/tools/tools_mcp_component.py,sha256=id6PptzVb06AAb6-ydxGDQPHH5gFGn9RzchZvUOeug0,12871
|
|
45
|
+
ha_mcp/tools/tools_registry.py,sha256=xIhIYeKr-sOC2bP8uglgL-576BoDnweBHmzhwk-KPzQ,46054
|
|
46
|
+
ha_mcp/tools/tools_resources.py,sha256=4s6aflsV7eP5vkz_LHC960OhzUizfQPyocTPBNyc_-k,22945
|
|
47
|
+
ha_mcp/tools/tools_search.py,sha256=4p3I5f6DPeix_KWgwQxC3I-nEokEze3JcntohzoCkos,26319
|
|
48
|
+
ha_mcp/tools/tools_service.py,sha256=eylo6w0mxjv3JcufCZPZre5U_D4izmc8q20ML66nwU0,9062
|
|
49
|
+
ha_mcp/tools/tools_services.py,sha256=KgWt75Rnnk2XQHOgZecf0vOFR0vNO1LFKWh6Mj39aTw,10895
|
|
50
|
+
ha_mcp/tools/tools_system.py,sha256=uHu8CVsALjgpVG21h3vEnntmB3sNP0yGD6d01e9xFdM,14210
|
|
51
|
+
ha_mcp/tools/tools_todo.py,sha256=l71w_ln_Xjy7PbQ5k4Qcf15OJvNFEeihWxyQxfHkmLg,19851
|
|
52
|
+
ha_mcp/tools/tools_traces.py,sha256=F_ppsGNqOEmnav3xgPtQSer8NykzUnArEs2R3qO6K2A,18548
|
|
53
|
+
ha_mcp/tools/tools_updates.py,sha256=Va8OD1h53TODbrtqKkLMAeLm8ZQaNwwAfPSwHsiPgCo,18357
|
|
54
|
+
ha_mcp/tools/tools_utility.py,sha256=OIuxFx-SdTsenRyChlTDNTtOS8iJLkj59IrGq2UyefQ,20886
|
|
55
|
+
ha_mcp/tools/tools_voice_assistant.py,sha256=ylcoFQzYFGSeRN3ULKUwk938Q7ykXtOPILhcZGwzKzQ,13158
|
|
56
|
+
ha_mcp/tools/tools_zones.py,sha256=M1hzXSrC59-kb3aFTNR9x8LbjOv6xVpdOyhdXs5ckt8,14083
|
|
57
|
+
ha_mcp/tools/util_helpers.py,sha256=yuey0tLVo5MZm_-kkDzp1rxYz27PMx6P7lttdlSpTw0,6164
|
|
58
|
+
ha_mcp/utils/__init__.py,sha256=mSXOwk11PxZeFLE7kygZsL53JSfZ53pFZ7MhjbJNaGQ,736
|
|
59
|
+
ha_mcp/utils/domain_handlers.py,sha256=k99mzMDnhCkptIdub8pbZq7RN3hrAjzySBWfd6uSC9I,12334
|
|
60
|
+
ha_mcp/utils/fuzzy_search.py,sha256=QD3Kd-MulX4japp9tj888eMoifTZvILV_iSogWAd8Pc,11377
|
|
61
|
+
ha_mcp/utils/operation_manager.py,sha256=cFW_PFuvU221IqQ2wZDc7o9gf2mffAX_EtePh7PTbhA,14332
|
|
62
|
+
ha_mcp/utils/usage_logger.py,sha256=dHkHzBCCrLMM1n0aZEWjVgOYjMtQvS3FGMRMbIcgUvw,9303
|
|
63
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/licenses/LICENSE,sha256=7rJXXKBJWgJF8595wk-YTxwVTEi1kQaIqyy9dh5o_oY,1062
|
|
64
|
+
tests/__init__.py,sha256=YRpec-ZFYCJ48oD_7ZcNY7dB8avoTWOrZICjaM-BYJ0,39
|
|
65
|
+
tests/test_constants.py,sha256=F14Pf5QMzG77RhsecaNWWaEL-B_8ykHJLIvVMcJxT8M,609
|
|
66
|
+
tests/test_env_manager.py,sha256=patnM4If8gLUGuFRQEYMAuNhsAvjLKZZYuR2oJ2c2kU,12011
|
|
67
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/METADATA,sha256=M2ztuZQ9Gkl2R3a2d6VnOVnTwO_p5O3nA0HndzBksLg,11677
|
|
68
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
69
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/entry_points.txt,sha256=MN0GhQEca3hlfJIBAclzzbvwU7K-czkhlc55Gn58vdg,254
|
|
70
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/top_level.txt,sha256=cqJLEmgh4gQBKg_vBqj0ahS4DCg4J0qBXYgZCDQ2IWs,13
|
|
71
|
+
ha_mcp_dev-6.3.1.dev137.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Julien
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
tests/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Home Assistant MCP Server Tests."""
|
tests/test_constants.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test constants shared across test modules.
|
|
3
|
+
|
|
4
|
+
This module centralizes test configuration values to ensure consistency
|
|
5
|
+
across all test environments.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Long-lived access token for test Home Assistant instance
|
|
9
|
+
# This token is embedded in tests/initial_test_state/.storage/auth
|
|
10
|
+
# Expires: 2035 (10+ years from token creation)
|
|
11
|
+
TEST_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxOTE5ZTZlMTVkYjI0Mzk2YTQ4YjFiZTI1MDM1YmU2YSIsImlhdCI6MTc1NzI4OTc5NiwiZXhwIjoyMDcyNjQ5Nzk2fQ.Yp9SSAjm2gvl9Xcu96FFxS8SapHxWAVzaI0E3cD9xac"
|
|
12
|
+
|
|
13
|
+
# Test user credentials (for UI access)
|
|
14
|
+
TEST_USER = "mcp"
|
|
15
|
+
TEST_PASSWORD = "mcp"
|