ha-mcp-dev 7.1.0.dev291__tar.gz → 7.1.0.dev293__tar.gz
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_dev-7.1.0.dev291/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.1.0.dev293}/PKG-INFO +1 -2
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/pyproject.toml +1 -2
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/config.py +1 -1
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/smart_search.py +20 -6
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_dashboards.py +19 -237
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -2
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/requires.txt +0 -3
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/LICENSE +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/README.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/setup.cfg +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/card_types.json +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/dashboard_guide.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_info.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/tests/test_env_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.1.0.
|
|
3
|
+
Version: 7.1.0.dev293
|
|
4
4
|
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
5
|
Author-email: Julien <github@qc-h.net>
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,6 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: fastmcp==3.1.1
|
|
22
22
|
Requires-Dist: httpx[socks]==0.28.1
|
|
23
|
-
Requires-Dist: jq==1.11.0; sys_platform != "win32"
|
|
24
23
|
Requires-Dist: pydantic==2.12.5
|
|
25
24
|
Requires-Dist: python-dotenv==1.2.2
|
|
26
25
|
Requires-Dist: truststore==0.10.4
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.1.0.
|
|
7
|
+
version = "7.1.0.dev293"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
|
@@ -26,7 +26,6 @@ classifiers = [
|
|
|
26
26
|
dependencies = [
|
|
27
27
|
"fastmcp==3.1.1",
|
|
28
28
|
"httpx[socks]==0.28.1",
|
|
29
|
-
'jq==1.11.0; sys_platform != "win32"',
|
|
30
29
|
"pydantic==2.12.5",
|
|
31
30
|
"python-dotenv==1.2.2",
|
|
32
31
|
"truststore==0.10.4",
|
|
@@ -80,7 +80,7 @@ class Settings(BaseSettings):
|
|
|
80
80
|
# Examples: "tools_config_automations,tools_config_scripts,tools_traces"
|
|
81
81
|
enabled_tool_modules: str = Field("all", alias="ENABLED_TOOL_MODULES")
|
|
82
82
|
|
|
83
|
-
# Dashboard partial update tools (
|
|
83
|
+
# Dashboard partial update tools (python_transform, find_card)
|
|
84
84
|
# These are token-efficient alternatives to full config replacement.
|
|
85
85
|
# Disable when using clients with programmatic tool use (future).
|
|
86
86
|
enable_dashboard_partial_tools: bool = Field(True, alias="ENABLE_DASHBOARD_PARTIAL_TOOLS")
|
|
@@ -4,6 +4,7 @@ Smart search tools for Home Assistant MCP server.
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
+
import random
|
|
7
8
|
import time
|
|
8
9
|
from typing import Any
|
|
9
10
|
|
|
@@ -387,9 +388,20 @@ class SmartSearchTools:
|
|
|
387
388
|
return_exceptions=True,
|
|
388
389
|
)
|
|
389
390
|
|
|
390
|
-
#
|
|
391
|
-
|
|
392
|
-
|
|
391
|
+
# Entities are mandatory — surface connection/auth errors immediately.
|
|
392
|
+
# Services failure is logged at warning (affects total count and service catalog).
|
|
393
|
+
# Registry failures are logged at debug (area enrichment only).
|
|
394
|
+
if isinstance(results[0], Exception):
|
|
395
|
+
raise results[0]
|
|
396
|
+
|
|
397
|
+
entities = results[0]
|
|
398
|
+
partial_warnings: list[str] = []
|
|
399
|
+
if isinstance(results[1], Exception):
|
|
400
|
+
logger.warning(f"Could not fetch services: {results[1]}")
|
|
401
|
+
partial_warnings.append(f"Services unavailable: {results[1]}")
|
|
402
|
+
services = []
|
|
403
|
+
else:
|
|
404
|
+
services = results[1]
|
|
393
405
|
|
|
394
406
|
# Handle area registry result
|
|
395
407
|
area_registry: list[dict[str, Any]] = []
|
|
@@ -537,8 +549,6 @@ class SmartSearchTools:
|
|
|
537
549
|
}
|
|
538
550
|
|
|
539
551
|
# Prepare domain stats with entity filtering and truncation info
|
|
540
|
-
import random
|
|
541
|
-
|
|
542
552
|
formatted_domain_stats = {}
|
|
543
553
|
for domain, stats in sorted_domains:
|
|
544
554
|
all_entities = stats["all_entities"]
|
|
@@ -569,7 +579,7 @@ class SmartSearchTools:
|
|
|
569
579
|
}
|
|
570
580
|
|
|
571
581
|
# Build base response
|
|
572
|
-
base_response = {
|
|
582
|
+
base_response: dict[str, Any] = {
|
|
573
583
|
"success": True,
|
|
574
584
|
"system_summary": {
|
|
575
585
|
"total_entities": len(entities),
|
|
@@ -582,6 +592,10 @@ class SmartSearchTools:
|
|
|
582
592
|
"ai_insights": ai_insights,
|
|
583
593
|
}
|
|
584
594
|
|
|
595
|
+
if partial_warnings:
|
|
596
|
+
base_response["partial"] = True
|
|
597
|
+
base_response["warnings"] = partial_warnings
|
|
598
|
+
|
|
585
599
|
# Add level-specific fields
|
|
586
600
|
if detail_level == "full":
|
|
587
601
|
# Full: Add device types and service catalog
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
@@ -28,26 +28,6 @@ from .util_helpers import parse_json_param
|
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
31
|
-
# Try to import jq - it's not available on Windows ARM64
|
|
32
|
-
try:
|
|
33
|
-
import jq # noqa: F401
|
|
34
|
-
|
|
35
|
-
JQ_AVAILABLE = True
|
|
36
|
-
except ImportError:
|
|
37
|
-
JQ_AVAILABLE = False
|
|
38
|
-
logger.warning(
|
|
39
|
-
"jq library not available - jq_transform features will be disabled. "
|
|
40
|
-
"This is expected on Windows ARM64 where jq cannot be compiled."
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
# Error message when jq_transform is used without jq available
|
|
44
|
-
_JQ_UNAVAILABLE_ERROR = (
|
|
45
|
-
"jq_transform is not available - jq library could not be imported. "
|
|
46
|
-
"This is a known limitation on Windows ARM64 where jq cannot be compiled. "
|
|
47
|
-
"Please use the 'config' parameter for full config replacement instead, "
|
|
48
|
-
"or use ha-mcp on Windows x64, Linux, or macOS where jq is supported."
|
|
49
|
-
)
|
|
50
|
-
|
|
51
31
|
# Card documentation base URL
|
|
52
32
|
CARD_DOCS_BASE_URL = (
|
|
53
33
|
"https://raw.githubusercontent.com/home-assistant/home-assistant.io/"
|
|
@@ -127,47 +107,6 @@ async def _verify_config_unchanged(
|
|
|
127
107
|
return {"success": True}
|
|
128
108
|
|
|
129
109
|
|
|
130
|
-
def _apply_jq_transform(
|
|
131
|
-
config: dict[str, Any], expression: str
|
|
132
|
-
) -> tuple[dict[str, Any] | None, str | None]:
|
|
133
|
-
"""
|
|
134
|
-
Apply a jq transformation to dashboard config.
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
tuple: (transformed_config, error_message)
|
|
138
|
-
- On success: (dict, None)
|
|
139
|
-
- On failure: (None, error_string)
|
|
140
|
-
"""
|
|
141
|
-
# Check if jq is available
|
|
142
|
-
if not JQ_AVAILABLE:
|
|
143
|
-
return None, _JQ_UNAVAILABLE_ERROR
|
|
144
|
-
|
|
145
|
-
import jq
|
|
146
|
-
|
|
147
|
-
try:
|
|
148
|
-
# Compile and validate the jq expression
|
|
149
|
-
program = jq.compile(expression)
|
|
150
|
-
except ValueError as e:
|
|
151
|
-
return None, f"Invalid jq expression: {e}"
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
# Execute the transformation
|
|
155
|
-
result = program.input_value(config).first()
|
|
156
|
-
except StopIteration:
|
|
157
|
-
return None, "jq expression produced no output"
|
|
158
|
-
except Exception as e:
|
|
159
|
-
return None, f"jq transformation error: {e}"
|
|
160
|
-
|
|
161
|
-
# Validate result is still a valid dashboard structure
|
|
162
|
-
if not isinstance(result, dict):
|
|
163
|
-
return None, f"jq result must be a dict, got {type(result).__name__}"
|
|
164
|
-
|
|
165
|
-
if "views" not in result and "strategy" not in result:
|
|
166
|
-
return None, "jq result missing required 'views' or 'strategy' key"
|
|
167
|
-
|
|
168
|
-
return result, None
|
|
169
|
-
|
|
170
|
-
|
|
171
110
|
def _find_cards_in_config(
|
|
172
111
|
config: dict[str, Any],
|
|
173
112
|
entity_id: str | None = None,
|
|
@@ -393,11 +332,11 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
393
332
|
if config_size >= 10000:
|
|
394
333
|
result["hint"] = (
|
|
395
334
|
f"Large config ({config_size:,} bytes). For edits, use "
|
|
396
|
-
"ha_dashboard_find_card() + ha_config_set_dashboard(
|
|
335
|
+
"ha_dashboard_find_card() + ha_config_set_dashboard(python_transform=...) "
|
|
397
336
|
"instead of full config replacement."
|
|
398
337
|
)
|
|
399
338
|
|
|
400
|
-
return
|
|
339
|
+
return result
|
|
401
340
|
except ToolError:
|
|
402
341
|
raise
|
|
403
342
|
except Exception as e:
|
|
@@ -438,26 +377,14 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
438
377
|
description="Dashboard configuration with views and cards. "
|
|
439
378
|
"Can be dict or JSON string. "
|
|
440
379
|
"Omit or set to None to create dashboard without initial config. "
|
|
441
|
-
"Mutually exclusive with
|
|
442
|
-
),
|
|
443
|
-
] = None,
|
|
444
|
-
jq_transform: Annotated[
|
|
445
|
-
str | None,
|
|
446
|
-
Field(
|
|
447
|
-
description="jq expression to transform existing dashboard config. "
|
|
448
|
-
"Mutually exclusive with config and python_transform. Requires config_hash for validation. "
|
|
449
|
-
"Examples: '.views[0].sections[1].cards[0].icon = \"mdi:thermometer\"', "
|
|
450
|
-
'\'.views[0].cards += [{"type": "button", "entity": "light.bedroom"}]\', '
|
|
451
|
-
"'del(.views[0].sections[0].cards[2])'. "
|
|
452
|
-
"MULTI-OP: Chain with '|': 'del(.views[0].cards[2]) | .views[0].cards[0].icon = \"mdi:new\"'. "
|
|
453
|
-
"Use ha_dashboard_find_card() to get jq_path for targeted edits."
|
|
380
|
+
"Mutually exclusive with python_transform."
|
|
454
381
|
),
|
|
455
382
|
] = None,
|
|
456
383
|
python_transform: Annotated[
|
|
457
384
|
str | None,
|
|
458
385
|
Field(
|
|
459
386
|
description="Python expression to transform existing dashboard config. "
|
|
460
|
-
"Mutually exclusive with config
|
|
387
|
+
"Mutually exclusive with config. "
|
|
461
388
|
"Requires config_hash for validation. "
|
|
462
389
|
"See PYTHON TRANSFORM SECURITY below for allowed operations. "
|
|
463
390
|
"Examples: "
|
|
@@ -471,7 +398,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
471
398
|
str | None,
|
|
472
399
|
Field(
|
|
473
400
|
description="Config hash from ha_config_get_dashboard for optimistic locking. "
|
|
474
|
-
"REQUIRED for
|
|
401
|
+
"REQUIRED for python_transform (validates dashboard unchanged). "
|
|
475
402
|
"Optional for config (validates before full replacement if provided)."
|
|
476
403
|
),
|
|
477
404
|
] = None,
|
|
@@ -505,31 +432,20 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
505
432
|
Create or update a Home Assistant dashboard.
|
|
506
433
|
|
|
507
434
|
Creates a new dashboard or updates an existing one with the provided configuration.
|
|
508
|
-
Supports
|
|
435
|
+
Supports two modes: full config replacement OR Python transformation.
|
|
509
436
|
|
|
510
437
|
Use 'default' or 'lovelace' to target the built-in default dashboard.
|
|
511
438
|
New dashboards require a hyphenated url_path (e.g., 'my-dashboard').
|
|
512
439
|
|
|
513
440
|
WHEN TO USE WHICH MODE:
|
|
514
441
|
- python_transform: RECOMMENDED for edits. Surgical/pattern-based updates, works on all platforms.
|
|
515
|
-
- jq_transform: Legacy mode. Requires jq binary (not available on Windows ARM64).
|
|
516
442
|
- config: New dashboards only, or full restructure. Replaces everything.
|
|
517
443
|
|
|
518
|
-
|
|
519
|
-
- Update card icon: '.views[0].sections[1].cards[0].icon = "mdi:thermometer"'
|
|
520
|
-
- Add card: '.views[0].cards += [{"type": "button", "entity": "light.bedroom"}]'
|
|
521
|
-
- Delete card: 'del(.views[0].sections[0].cards[2])'
|
|
522
|
-
- Update by selection: '(.views[0].cards[] | select(.entity == "light.living_room")).icon = "mdi:lamp"'
|
|
523
|
-
|
|
524
|
-
MULTI-OPERATION (chain with |):
|
|
525
|
-
- Delete then update: 'del(.views[0].cards[2]) | .views[0].cards[0].icon = "mdi:new"'
|
|
526
|
-
- Multiple updates: '.views[0].cards[0].icon = "mdi:a" | .views[0].cards[1].icon = "mdi:b"'
|
|
527
|
-
|
|
528
|
-
IMPORTANT: After delete/add operations, indices shift! Subsequent jq_transform calls
|
|
444
|
+
IMPORTANT: After delete/add operations, indices shift! Subsequent python_transform calls
|
|
529
445
|
must use fresh config_hash from ha_dashboard_find_card() or ha_config_get_dashboard()
|
|
530
446
|
to get updated structure. Chain multiple ops in ONE expression when possible.
|
|
531
447
|
|
|
532
|
-
TIP: Use ha_dashboard_find_card() to get the
|
|
448
|
+
TIP: Use ha_dashboard_find_card() to get the path for any card.
|
|
533
449
|
|
|
534
450
|
PYTHON TRANSFORM EXAMPLES (RECOMMENDED):
|
|
535
451
|
- Update card icon: 'config["views"][0]["cards"][0]["icon"] = "mdi:thermometer"'
|
|
@@ -587,12 +503,6 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
587
503
|
}
|
|
588
504
|
)
|
|
589
505
|
|
|
590
|
-
Update card using jq_transform (efficient for small changes):
|
|
591
|
-
ha_config_set_dashboard(
|
|
592
|
-
url_path="home-dashboard",
|
|
593
|
-
jq_transform='.views[0].sections[0].cards[0].features += [{"type": "climate-hvac-modes"}]'
|
|
594
|
-
)
|
|
595
|
-
|
|
596
506
|
Create strategy-based dashboard (auto-generated):
|
|
597
507
|
ha_config_set_dashboard(
|
|
598
508
|
url_path="my-home",
|
|
@@ -645,24 +555,15 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
645
555
|
context={"action": "set", "url_path": url_path},
|
|
646
556
|
))
|
|
647
557
|
|
|
648
|
-
# Validate mutual exclusivity of config
|
|
649
|
-
|
|
650
|
-
[
|
|
651
|
-
config is not None,
|
|
652
|
-
jq_transform is not None,
|
|
653
|
-
python_transform is not None,
|
|
654
|
-
]
|
|
655
|
-
)
|
|
656
|
-
|
|
657
|
-
if transforms_provided > 1:
|
|
558
|
+
# Validate mutual exclusivity of config and python_transform
|
|
559
|
+
if config is not None and python_transform is not None:
|
|
658
560
|
raise_tool_error(create_error_response(
|
|
659
561
|
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
660
|
-
"Cannot use
|
|
562
|
+
"Cannot use both config and python_transform simultaneously",
|
|
661
563
|
suggestions=[
|
|
662
|
-
"Use only ONE of: config
|
|
564
|
+
"Use only ONE of: config or python_transform",
|
|
663
565
|
"config: Full replacement",
|
|
664
|
-
"
|
|
665
|
-
"python_transform: Python-based edits (recommended, works everywhere)",
|
|
566
|
+
"python_transform: Python-based edits (recommended)",
|
|
666
567
|
],
|
|
667
568
|
context={"action": "set", "url_path": url_path},
|
|
668
569
|
))
|
|
@@ -783,125 +684,6 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
783
684
|
"message": f"Dashboard {url_path} updated via Python transform",
|
|
784
685
|
}
|
|
785
686
|
|
|
786
|
-
# Handle jq_transform mode
|
|
787
|
-
if jq_transform is not None:
|
|
788
|
-
# config_hash is REQUIRED for jq_transform
|
|
789
|
-
if config_hash is None:
|
|
790
|
-
raise_tool_error(create_error_response(
|
|
791
|
-
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
792
|
-
"config_hash is required for jq_transform",
|
|
793
|
-
suggestions=[
|
|
794
|
-
"Call ha_config_get_dashboard() or ha_dashboard_find_card() first",
|
|
795
|
-
"Use the config_hash from that response",
|
|
796
|
-
],
|
|
797
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
798
|
-
))
|
|
799
|
-
|
|
800
|
-
# Fetch current dashboard config
|
|
801
|
-
get_data = {"type": "lovelace/config", "force": True}
|
|
802
|
-
if url_path:
|
|
803
|
-
get_data["url_path"] = url_path
|
|
804
|
-
|
|
805
|
-
response = await client.send_websocket_message(get_data)
|
|
806
|
-
|
|
807
|
-
if isinstance(response, dict) and not response.get("success", True):
|
|
808
|
-
error_msg = response.get("error", {})
|
|
809
|
-
if isinstance(error_msg, dict):
|
|
810
|
-
error_msg = error_msg.get("message", str(error_msg))
|
|
811
|
-
raise_tool_error(create_error_response(
|
|
812
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
813
|
-
f"Dashboard not found or inaccessible: {error_msg}",
|
|
814
|
-
suggestions=[
|
|
815
|
-
"jq_transform requires an existing dashboard",
|
|
816
|
-
"Use 'config' parameter to create a new dashboard",
|
|
817
|
-
"Verify dashboard exists with ha_config_get_dashboard(list_only=True)",
|
|
818
|
-
],
|
|
819
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
820
|
-
))
|
|
821
|
-
|
|
822
|
-
current_config = (
|
|
823
|
-
response.get("result") if isinstance(response, dict) else response
|
|
824
|
-
)
|
|
825
|
-
if not isinstance(current_config, dict):
|
|
826
|
-
raise_tool_error(create_error_response(
|
|
827
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
828
|
-
"Current dashboard config is invalid",
|
|
829
|
-
suggestions=[
|
|
830
|
-
"Initialize dashboard with 'config' parameter first"
|
|
831
|
-
],
|
|
832
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
833
|
-
))
|
|
834
|
-
|
|
835
|
-
# Validate config_hash for optimistic locking
|
|
836
|
-
current_hash = _compute_config_hash(current_config)
|
|
837
|
-
if current_hash != config_hash:
|
|
838
|
-
raise_tool_error(create_error_response(
|
|
839
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
840
|
-
"Dashboard modified since last read (conflict)",
|
|
841
|
-
suggestions=[
|
|
842
|
-
"Call ha_config_get_dashboard() or ha_dashboard_find_card() again",
|
|
843
|
-
"Use the fresh config_hash from that response",
|
|
844
|
-
"Indices may have changed - re-locate cards with ha_dashboard_find_card()",
|
|
845
|
-
],
|
|
846
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
847
|
-
))
|
|
848
|
-
|
|
849
|
-
# Apply jq transformation
|
|
850
|
-
jq_result, error = _apply_jq_transform(
|
|
851
|
-
current_config, jq_transform
|
|
852
|
-
)
|
|
853
|
-
if error:
|
|
854
|
-
raise_tool_error(create_error_response(
|
|
855
|
-
ErrorCode.VALIDATION_FAILED,
|
|
856
|
-
error,
|
|
857
|
-
suggestions=[
|
|
858
|
-
"Verify jq syntax: https://jqlang.github.io/jq/manual/",
|
|
859
|
-
"Use ha_dashboard_find_card() to get correct jq_path",
|
|
860
|
-
"Test expression locally: echo '<config>' | jq '<expression>'",
|
|
861
|
-
],
|
|
862
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
863
|
-
))
|
|
864
|
-
transformed_config = cast(dict[str, Any], jq_result)
|
|
865
|
-
|
|
866
|
-
# Save transformed config
|
|
867
|
-
save_data = {
|
|
868
|
-
"type": "lovelace/config/save",
|
|
869
|
-
"config": transformed_config,
|
|
870
|
-
}
|
|
871
|
-
if url_path:
|
|
872
|
-
save_data["url_path"] = url_path
|
|
873
|
-
|
|
874
|
-
save_result = await client.send_websocket_message(save_data)
|
|
875
|
-
|
|
876
|
-
if isinstance(save_result, dict) and not save_result.get(
|
|
877
|
-
"success", True
|
|
878
|
-
):
|
|
879
|
-
error_msg = save_result.get("error", {})
|
|
880
|
-
if isinstance(error_msg, dict):
|
|
881
|
-
error_msg = error_msg.get("message", str(error_msg))
|
|
882
|
-
raise_tool_error(create_error_response(
|
|
883
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
884
|
-
f"Failed to save transformed config: {error_msg}",
|
|
885
|
-
suggestions=[
|
|
886
|
-
"jq expression may have produced invalid dashboard structure",
|
|
887
|
-
"Verify config format is valid Lovelace JSON",
|
|
888
|
-
],
|
|
889
|
-
context={"action": "jq_transform", "url_path": url_path},
|
|
890
|
-
))
|
|
891
|
-
|
|
892
|
-
# Compute new hash for potential chaining
|
|
893
|
-
# transformed_config is guaranteed to be a dict here (validated above)
|
|
894
|
-
new_config_hash = _compute_config_hash(transformed_config)
|
|
895
|
-
|
|
896
|
-
return {
|
|
897
|
-
"success": True,
|
|
898
|
-
"action": "jq_transform",
|
|
899
|
-
"url_path": url_path,
|
|
900
|
-
"config_hash": new_config_hash,
|
|
901
|
-
"jq_expression": jq_transform,
|
|
902
|
-
"message": f"Dashboard {url_path} updated via jq transform",
|
|
903
|
-
}
|
|
904
|
-
|
|
905
687
|
# Check if dashboard exists
|
|
906
688
|
result = await client.send_websocket_message(
|
|
907
689
|
{"type": "lovelace/dashboards/list"}
|
|
@@ -1067,7 +849,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1067
849
|
if existing_config_size >= 10000:
|
|
1068
850
|
hint = (
|
|
1069
851
|
f"Replaced large config ({existing_config_size:,} bytes). "
|
|
1070
|
-
"Consider
|
|
852
|
+
"Consider python_transform for targeted edits."
|
|
1071
853
|
)
|
|
1072
854
|
|
|
1073
855
|
# Build save config message
|
|
@@ -1421,7 +1203,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1421
1203
|
|
|
1422
1204
|
# =========================================================================
|
|
1423
1205
|
# Card Search Tool (partial update tools - controlled by feature flag)
|
|
1424
|
-
# Card add/update/remove replaced by
|
|
1206
|
+
# Card add/update/remove replaced by python_transform in ha_config_set_dashboard
|
|
1425
1207
|
# =========================================================================
|
|
1426
1208
|
|
|
1427
1209
|
# Check feature flag for partial update tools (lazy check, default enabled)
|
|
@@ -1476,8 +1258,8 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1476
1258
|
"""
|
|
1477
1259
|
Find cards in a dashboard by entity_id, type, or heading text.
|
|
1478
1260
|
|
|
1479
|
-
Returns card locations (view_index, section_index, card_index) and
|
|
1480
|
-
for use with ha_config_set_dashboard(
|
|
1261
|
+
Returns card locations (view_index, section_index, card_index) and path
|
|
1262
|
+
for use with ha_config_set_dashboard(python_transform=...).
|
|
1481
1263
|
|
|
1482
1264
|
Use this tool BEFORE targeted updates to find exact card positions without
|
|
1483
1265
|
manually parsing the full dashboard config.
|
|
@@ -1509,7 +1291,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1509
1291
|
3. ha_config_set_dashboard(
|
|
1510
1292
|
url_path="my-dash",
|
|
1511
1293
|
config_hash=find["config_hash"],
|
|
1512
|
-
|
|
1294
|
+
python_transform=f'config{find["matches"][0]["jq_path"]}["icon"] = "mdi:lamp"'
|
|
1513
1295
|
)
|
|
1514
1296
|
"""
|
|
1515
1297
|
try:
|
|
@@ -1593,7 +1375,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1593
1375
|
},
|
|
1594
1376
|
"matches": matches,
|
|
1595
1377
|
"match_count": len(matches),
|
|
1596
|
-
"hint": "Use jq_path with ha_config_set_dashboard(
|
|
1378
|
+
"hint": "Use jq_path with ha_config_set_dashboard(python_transform=...) for targeted updates"
|
|
1597
1379
|
if matches
|
|
1598
1380
|
else "No matches found. Try broader search criteria.",
|
|
1599
1381
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.1.0.
|
|
3
|
+
Version: 7.1.0.dev293
|
|
4
4
|
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
5
|
Author-email: Julien <github@qc-h.net>
|
|
6
6
|
License: MIT
|
|
@@ -20,7 +20,6 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: fastmcp==3.1.1
|
|
22
22
|
Requires-Dist: httpx[socks]==0.28.1
|
|
23
|
-
Requires-Dist: jq==1.11.0; sys_platform != "win32"
|
|
24
23
|
Requires-Dist: pydantic==2.12.5
|
|
25
24
|
Requires-Dist: python-dotenv==1.2.2
|
|
26
25
|
Requires-Dist: truststore==0.10.4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.1.0.dev291 → ha_mcp_dev-7.1.0.dev293}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|