ha-mcp-dev 7.5.0.dev512__tar.gz → 7.5.0.dev513__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.5.0.dev512/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev513}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/pyproject.toml +1 -1
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_dashboards.py +106 -63
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/setup.cfg +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/tests/test_env_manager.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.5.0.
|
|
7
|
+
version = "7.5.0.dev513"
|
|
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"
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
@@ -21,12 +21,68 @@ from ..utils.python_sandbox import (
|
|
|
21
21
|
get_security_documentation,
|
|
22
22
|
safe_execute,
|
|
23
23
|
)
|
|
24
|
-
from .helpers import
|
|
24
|
+
from .helpers import (
|
|
25
|
+
exception_to_structured_error,
|
|
26
|
+
extract_tool_error_message,
|
|
27
|
+
log_tool_usage,
|
|
28
|
+
raise_tool_error,
|
|
29
|
+
)
|
|
25
30
|
from .util_helpers import parse_json_param
|
|
26
31
|
|
|
27
32
|
logger = logging.getLogger(__name__)
|
|
28
33
|
|
|
29
34
|
|
|
35
|
+
async def _get_dashboard_config_internal(
|
|
36
|
+
client: Any, url_path: str | None
|
|
37
|
+
) -> tuple[dict[str, Any], str]:
|
|
38
|
+
"""Fetch dashboard config from HA and compute its hash.
|
|
39
|
+
|
|
40
|
+
Returns ``(config, config_hash)`` tuple where ``config`` is the
|
|
41
|
+
authoritative Lovelace config dict returned by HA's ``lovelace/config``
|
|
42
|
+
WebSocket call (with ``force=True`` to bypass any cache) and
|
|
43
|
+
``config_hash`` is computed from that config via ``compute_config_hash``.
|
|
44
|
+
|
|
45
|
+
Used internally to obtain the authoritative post-save hash and as the
|
|
46
|
+
fetch+hash building block for the optimistic-locking pre-read paths.
|
|
47
|
+
Mirrors the ``_get_<entity>_config_internal`` helpers in the sibling
|
|
48
|
+
files (``tools_config_scripts.py``, ``tools_config_automations.py``,
|
|
49
|
+
``tools_config_scenes.py``).
|
|
50
|
+
|
|
51
|
+
Raises ``ToolError`` with ``ErrorCode.SERVICE_CALL_FAILED`` if the
|
|
52
|
+
WebSocket call reports failure or the response is not a dict; callers
|
|
53
|
+
can rely on the returned tuple being populated.
|
|
54
|
+
"""
|
|
55
|
+
get_data: dict[str, Any] = {"type": "lovelace/config", "force": True}
|
|
56
|
+
if url_path:
|
|
57
|
+
get_data["url_path"] = url_path
|
|
58
|
+
|
|
59
|
+
response = await client.send_websocket_message(get_data)
|
|
60
|
+
|
|
61
|
+
if isinstance(response, dict) and not response.get("success", True):
|
|
62
|
+
error_msg = response.get("error", {})
|
|
63
|
+
if isinstance(error_msg, dict):
|
|
64
|
+
error_msg = error_msg.get("message", str(error_msg))
|
|
65
|
+
raise_tool_error(
|
|
66
|
+
create_error_response(
|
|
67
|
+
ErrorCode.SERVICE_CALL_FAILED,
|
|
68
|
+
f"Dashboard fetch failed: {error_msg}",
|
|
69
|
+
context={"url_path": url_path},
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
config = response.get("result") if isinstance(response, dict) else response
|
|
74
|
+
if not isinstance(config, dict):
|
|
75
|
+
raise_tool_error(
|
|
76
|
+
create_error_response(
|
|
77
|
+
ErrorCode.SERVICE_CALL_FAILED,
|
|
78
|
+
"Dashboard config response was not a dict",
|
|
79
|
+
context={"url_path": url_path},
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return cast(dict[str, Any], config), compute_config_hash(config)
|
|
84
|
+
|
|
85
|
+
|
|
30
86
|
async def _verify_config_unchanged(
|
|
31
87
|
client: Any,
|
|
32
88
|
url_path: str,
|
|
@@ -1029,21 +1085,19 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1029
1085
|
)
|
|
1030
1086
|
)
|
|
1031
1087
|
|
|
1032
|
-
# Fetch current dashboard config
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if isinstance(error_msg, dict):
|
|
1042
|
-
error_msg = error_msg.get("message", str(error_msg))
|
|
1088
|
+
# Fetch current dashboard config + hash via the shared helper.
|
|
1089
|
+
# Re-wrap helper's generic fetch error with python_transform-
|
|
1090
|
+
# specific UX suggestions so the caller learns this branch
|
|
1091
|
+
# requires an existing dashboard.
|
|
1092
|
+
try:
|
|
1093
|
+
current_config, current_hash = await _get_dashboard_config_internal(
|
|
1094
|
+
client, url_path
|
|
1095
|
+
)
|
|
1096
|
+
except ToolError as e:
|
|
1043
1097
|
raise_tool_error(
|
|
1044
1098
|
create_error_response(
|
|
1045
1099
|
ErrorCode.SERVICE_CALL_FAILED,
|
|
1046
|
-
f"Dashboard not found or inaccessible: {
|
|
1100
|
+
f"Dashboard not found or inaccessible: {extract_tool_error_message(e)}",
|
|
1047
1101
|
suggestions=[
|
|
1048
1102
|
"python_transform requires an existing dashboard",
|
|
1049
1103
|
"Use 'config' parameter to create a new dashboard",
|
|
@@ -1056,26 +1110,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1056
1110
|
)
|
|
1057
1111
|
)
|
|
1058
1112
|
|
|
1059
|
-
current_config = (
|
|
1060
|
-
response.get("result") if isinstance(response, dict) else response
|
|
1061
|
-
)
|
|
1062
|
-
if not isinstance(current_config, dict):
|
|
1063
|
-
raise_tool_error(
|
|
1064
|
-
create_error_response(
|
|
1065
|
-
ErrorCode.SERVICE_CALL_FAILED,
|
|
1066
|
-
"Current dashboard config is invalid",
|
|
1067
|
-
suggestions=[
|
|
1068
|
-
"Initialize dashboard with 'config' parameter first"
|
|
1069
|
-
],
|
|
1070
|
-
context={
|
|
1071
|
-
"action": "python_transform",
|
|
1072
|
-
"url_path": url_path,
|
|
1073
|
-
},
|
|
1074
|
-
)
|
|
1075
|
-
)
|
|
1076
|
-
|
|
1077
1113
|
# Validate config_hash for optimistic locking
|
|
1078
|
-
current_hash = compute_config_hash(current_config)
|
|
1079
1114
|
if current_hash != config_hash:
|
|
1080
1115
|
raise_tool_error(
|
|
1081
1116
|
create_error_response(
|
|
@@ -1153,8 +1188,10 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1153
1188
|
)
|
|
1154
1189
|
)
|
|
1155
1190
|
|
|
1156
|
-
#
|
|
1157
|
-
new_config_hash =
|
|
1191
|
+
# Re-fetch to get authoritative hash (HA may normalize after save)
|
|
1192
|
+
_, new_config_hash = await _get_dashboard_config_internal(
|
|
1193
|
+
client, url_path
|
|
1194
|
+
)
|
|
1158
1195
|
|
|
1159
1196
|
transform_result: dict[str, Any] = {
|
|
1160
1197
|
"success": True,
|
|
@@ -1309,38 +1346,44 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1309
1346
|
|
|
1310
1347
|
# For existing dashboards, optionally validate config_hash and warn on large replacement
|
|
1311
1348
|
if dashboard_exists:
|
|
1312
|
-
# Fetch current config
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1349
|
+
# Fetch current config + hash via the shared helper.
|
|
1350
|
+
# Tolerate fetch failures here — full-config replacement
|
|
1351
|
+
# should still proceed even if the pre-read can't load
|
|
1352
|
+
# the current state (force-replace path). The strict
|
|
1353
|
+
# ``ToolError`` raised by the helper is downgraded to a
|
|
1354
|
+
# skip of both the optimistic-locking check and the
|
|
1355
|
+
# large-config soft warning, matching the prior
|
|
1356
|
+
# silently-fall-through behaviour.
|
|
1357
|
+
# Distinct names from the python_transform branch's
|
|
1358
|
+
# ``current_config``/``current_hash`` so the optional
|
|
1359
|
+
# type here doesn't redefine the non-optional binding
|
|
1360
|
+
# mypy infers there.
|
|
1361
|
+
existing_config: dict[str, Any] | None = None
|
|
1362
|
+
existing_hash: str | None = None
|
|
1363
|
+
try:
|
|
1364
|
+
(
|
|
1365
|
+
existing_config,
|
|
1366
|
+
existing_hash,
|
|
1367
|
+
) = await _get_dashboard_config_internal(client, url_path)
|
|
1368
|
+
except ToolError:
|
|
1369
|
+
pass
|
|
1370
|
+
|
|
1371
|
+
if isinstance(existing_config, dict):
|
|
1372
|
+
existing_config_size = len(json.dumps(existing_config))
|
|
1328
1373
|
|
|
1329
1374
|
# Optional config_hash validation for full replacement
|
|
1330
|
-
if config_hash is not None:
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
"
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
],
|
|
1341
|
-
context={"action": "set", "url_path": url_path},
|
|
1342
|
-
)
|
|
1375
|
+
if config_hash is not None and existing_hash != config_hash:
|
|
1376
|
+
raise_tool_error(
|
|
1377
|
+
create_error_response(
|
|
1378
|
+
ErrorCode.SERVICE_CALL_FAILED,
|
|
1379
|
+
"Dashboard modified since last read (conflict)",
|
|
1380
|
+
suggestions=[
|
|
1381
|
+
"Call ha_config_get_dashboard() again",
|
|
1382
|
+
"Use the fresh config_hash, or omit config_hash to force replace",
|
|
1383
|
+
],
|
|
1384
|
+
context={"action": "set", "url_path": url_path},
|
|
1343
1385
|
)
|
|
1386
|
+
)
|
|
1344
1387
|
|
|
1345
1388
|
# Soft warning for large config full replacement (10KB ≈ 2-3k tokens)
|
|
1346
1389
|
if existing_config_size >= 10000:
|
|
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
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/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
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/transforms/lite_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev512 → ha_mcp_dev-7.5.0.dev513}/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
|
|
File without changes
|