ha-mcp-dev 7.5.0.dev531__tar.gz → 7.5.0.dev533__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.dev531/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.5.0.dev533}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/pyproject.toml +1 -1
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/smart_search.py +3 -7
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_automations.py +32 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_dashboards.py +54 -6
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_scripts.py +28 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_yaml_config.py +2 -15
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/setup.cfg +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/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.dev533"
|
|
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"
|
|
@@ -22,6 +22,7 @@ from ..utils.fuzzy_search import (
|
|
|
22
22
|
tokenize,
|
|
23
23
|
)
|
|
24
24
|
from .helpers import exception_to_structured_error, safe_info, safe_progress
|
|
25
|
+
from .tools_config_dashboards import fetch_dashboards_list
|
|
25
26
|
|
|
26
27
|
logger = logging.getLogger(__name__)
|
|
27
28
|
|
|
@@ -1773,14 +1774,9 @@ class SmartSearchTools:
|
|
|
1773
1774
|
if "dashboard" in search_types:
|
|
1774
1775
|
try:
|
|
1775
1776
|
# List all storage-mode dashboards
|
|
1776
|
-
|
|
1777
|
-
|
|
1777
|
+
dashboard_entries: list[dict[str, Any]] = (
|
|
1778
|
+
await fetch_dashboards_list(self.client) or []
|
|
1778
1779
|
)
|
|
1779
|
-
dashboard_entries: list[dict[str, Any]] = []
|
|
1780
|
-
if isinstance(dash_list_resp, dict) and dash_list_resp.get(
|
|
1781
|
-
"success"
|
|
1782
|
-
):
|
|
1783
|
-
dashboard_entries = dash_list_resp.get("result", [])
|
|
1784
1780
|
|
|
1785
1781
|
# Build list of dashboards to search (include default)
|
|
1786
1782
|
dashboards_to_search: list[tuple[str, str]] = [
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -289,6 +289,21 @@ class AutomationConfigTools:
|
|
|
289
289
|
For comprehensive automation documentation, use ha_get_skill_guide.
|
|
290
290
|
"""
|
|
291
291
|
try:
|
|
292
|
+
# Empty/whitespace identifier would propagate to the internal
|
|
293
|
+
# config lookup and surface as a misleading
|
|
294
|
+
# ``RESOURCE_NOT_FOUND``. Structured ``VALIDATION_INVALID_PARAMETER``
|
|
295
|
+
# naming the parameter is cleaner — extension of the #1312
|
|
296
|
+
# validate_identifier_not_empty pattern to the automations
|
|
297
|
+
# family per #1313.
|
|
298
|
+
validate_identifier_not_empty(
|
|
299
|
+
identifier,
|
|
300
|
+
"identifier",
|
|
301
|
+
suggestions=[
|
|
302
|
+
"Pass an automation entity_id (e.g. 'automation.morning_routine')",
|
|
303
|
+
"Or pass the unique_id of an existing automation",
|
|
304
|
+
"Use ha_search_entities(domain_filter='automation') to list automations",
|
|
305
|
+
],
|
|
306
|
+
)
|
|
292
307
|
normalized_config, config_hash = await self._get_automation_config_internal(identifier)
|
|
293
308
|
|
|
294
309
|
# Resolve entity_id and fetch category from entity registry
|
|
@@ -542,6 +557,23 @@ class AutomationConfigTools:
|
|
|
542
557
|
"""
|
|
543
558
|
bp_warnings: list[str] = []
|
|
544
559
|
try:
|
|
560
|
+
# ``identifier`` is optional (omit → create new with generated
|
|
561
|
+
# unique_id; pass → update existing). When provided, reject
|
|
562
|
+
# empty/whitespace up-front so the caller gets a structured
|
|
563
|
+
# parameter error instead of a misleading ``RESOURCE_NOT_FOUND``
|
|
564
|
+
# from the downstream lookup. The ``not identifier`` check
|
|
565
|
+
# further down the python_transform branch still handles the
|
|
566
|
+
# explicit ``identifier is None`` case for that mode.
|
|
567
|
+
if identifier is not None:
|
|
568
|
+
validate_identifier_not_empty(
|
|
569
|
+
identifier,
|
|
570
|
+
"identifier",
|
|
571
|
+
suggestions=[
|
|
572
|
+
"Omit identifier to create a new automation",
|
|
573
|
+
"Or pass a valid automation entity_id / unique_id to update",
|
|
574
|
+
],
|
|
575
|
+
context={"action": "set"},
|
|
576
|
+
)
|
|
545
577
|
# Validate mutual exclusivity of config and python_transform
|
|
546
578
|
if config is not None and python_transform is not None:
|
|
547
579
|
raise_tool_error(
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
@@ -26,6 +26,7 @@ from .helpers import (
|
|
|
26
26
|
extract_tool_error_message,
|
|
27
27
|
log_tool_usage,
|
|
28
28
|
raise_tool_error,
|
|
29
|
+
validate_identifier_not_empty,
|
|
29
30
|
)
|
|
30
31
|
from .util_helpers import parse_json_param
|
|
31
32
|
|
|
@@ -322,7 +323,7 @@ def _should_lazy_resolve(error_msg: str) -> bool:
|
|
|
322
323
|
return _LAZY_RESOLVE_TRIGGER in error_msg
|
|
323
324
|
|
|
324
325
|
|
|
325
|
-
async def
|
|
326
|
+
async def fetch_dashboards_list(
|
|
326
327
|
client: Any,
|
|
327
328
|
) -> list[dict[str, Any]] | None:
|
|
328
329
|
"""Fetch and normalise the lovelace/dashboards/list WebSocket response.
|
|
@@ -380,7 +381,7 @@ async def _resolve_dashboard(
|
|
|
380
381
|
to the registry id before issuing the delete. Discards
|
|
381
382
|
``dashboards``.
|
|
382
383
|
"""
|
|
383
|
-
dashboards = await
|
|
384
|
+
dashboards = await fetch_dashboards_list(client)
|
|
384
385
|
if dashboards is None:
|
|
385
386
|
return None, None
|
|
386
387
|
|
|
@@ -597,7 +598,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
597
598
|
try:
|
|
598
599
|
# List mode
|
|
599
600
|
if list_only:
|
|
600
|
-
dashboards = await
|
|
601
|
+
dashboards = await fetch_dashboards_list(client) or []
|
|
601
602
|
return {
|
|
602
603
|
"success": True,
|
|
603
604
|
"action": "list",
|
|
@@ -605,6 +606,25 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
605
606
|
"count": len(dashboards),
|
|
606
607
|
}
|
|
607
608
|
|
|
609
|
+
# ``url_path`` is optional in this tool (omitted with
|
|
610
|
+
# ``list_only=True`` lists all dashboards — handled above; omitted
|
|
611
|
+
# without ``list_only`` falls back to the default dashboard via
|
|
612
|
+
# the resolver below). When provided, reject empty/whitespace
|
|
613
|
+
# up-front so the caller gets a structured parameter error
|
|
614
|
+
# instead of a misleading ``RESOURCE_NOT_FOUND``. Extension of
|
|
615
|
+
# the #1312 validate_identifier_not_empty pattern to the
|
|
616
|
+
# dashboards family per #1313.
|
|
617
|
+
if url_path is not None:
|
|
618
|
+
validate_identifier_not_empty(
|
|
619
|
+
url_path,
|
|
620
|
+
"url_path",
|
|
621
|
+
suggestions=[
|
|
622
|
+
"Pass a dashboard URL path (e.g. 'lovelace-home')",
|
|
623
|
+
"Omit url_path and pass list_only=True to list dashboards",
|
|
624
|
+
"Use 'default' to target the default dashboard",
|
|
625
|
+
],
|
|
626
|
+
)
|
|
627
|
+
|
|
608
628
|
# Search mode — find cards, badges, or header cards
|
|
609
629
|
if search_mode:
|
|
610
630
|
get_data: dict[str, Any] = {"type": "lovelace/config", "force": True}
|
|
@@ -996,6 +1016,22 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
996
1016
|
are also updated if explicitly provided alongside (or instead of) a config change.
|
|
997
1017
|
"""
|
|
998
1018
|
try:
|
|
1019
|
+
# ``url_path`` is required (always non-None). Reject empty/
|
|
1020
|
+
# whitespace up-front so the caller gets a structured parameter
|
|
1021
|
+
# error instead of a misleading downstream failure (the
|
|
1022
|
+
# subsequent "default" alias, pre-resolver, and hyphen check
|
|
1023
|
+
# all assume a usable string). Extension of the #1312
|
|
1024
|
+
# validate_identifier_not_empty pattern to the dashboards
|
|
1025
|
+
# family per #1313.
|
|
1026
|
+
validate_identifier_not_empty(
|
|
1027
|
+
url_path,
|
|
1028
|
+
"url_path",
|
|
1029
|
+
suggestions=[
|
|
1030
|
+
"Pass a dashboard URL path (e.g. 'my-dashboard')",
|
|
1031
|
+
"Use 'default' or 'lovelace' for the default dashboard",
|
|
1032
|
+
],
|
|
1033
|
+
context={"action": "set"},
|
|
1034
|
+
)
|
|
999
1035
|
# Handle "default" as alias for the default dashboard
|
|
1000
1036
|
# (matches ha_config_get_dashboard behavior)
|
|
1001
1037
|
if url_path == "default":
|
|
@@ -1213,9 +1249,7 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1213
1249
|
if pre_fetched_dashboards is not None:
|
|
1214
1250
|
existing_dashboards = pre_fetched_dashboards
|
|
1215
1251
|
else:
|
|
1216
|
-
existing_dashboards = (
|
|
1217
|
-
await _fetch_dashboards_list(client) or []
|
|
1218
|
-
)
|
|
1252
|
+
existing_dashboards = await fetch_dashboards_list(client) or []
|
|
1219
1253
|
dashboard_exists = any(
|
|
1220
1254
|
d.get("url_path") == url_path for d in existing_dashboards
|
|
1221
1255
|
)
|
|
@@ -1490,6 +1524,20 @@ def register_config_dashboard_tools(mcp: Any, client: Any, **kwargs: Any) -> Non
|
|
|
1490
1524
|
Note: The default dashboard cannot be deleted via this method.
|
|
1491
1525
|
"""
|
|
1492
1526
|
try:
|
|
1527
|
+
# ``url_path`` is required. Reject empty/whitespace up-front so
|
|
1528
|
+
# the caller gets a structured parameter error instead of a
|
|
1529
|
+
# misleading "no dashboard found" from the resolver below.
|
|
1530
|
+
# Extension of the #1312 validate_identifier_not_empty pattern
|
|
1531
|
+
# to the dashboards family per #1313.
|
|
1532
|
+
validate_identifier_not_empty(
|
|
1533
|
+
url_path,
|
|
1534
|
+
"url_path",
|
|
1535
|
+
suggestions=[
|
|
1536
|
+
"Pass a dashboard URL path or internal ID (e.g. 'my-dashboard')",
|
|
1537
|
+
"Use ha_config_get_dashboard(list_only=True) to list dashboards",
|
|
1538
|
+
],
|
|
1539
|
+
context={"action": "delete"},
|
|
1540
|
+
)
|
|
1493
1541
|
resolved, _ = await _resolve_dashboard(client, url_path)
|
|
1494
1542
|
if resolved is None:
|
|
1495
1543
|
raise_tool_error(
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
@@ -103,6 +103,19 @@ class ConfigScriptTools:
|
|
|
103
103
|
For detailed script configuration help, use ha_get_skill_guide.
|
|
104
104
|
"""
|
|
105
105
|
try:
|
|
106
|
+
# Empty/whitespace script_id would propagate to
|
|
107
|
+
# ``get_script_config`` and surface as a misleading
|
|
108
|
+
# ``RESOURCE_NOT_FOUND``. Extension of the #1312
|
|
109
|
+
# validate_identifier_not_empty pattern to the scripts
|
|
110
|
+
# family per #1313.
|
|
111
|
+
validate_identifier_not_empty(
|
|
112
|
+
script_id,
|
|
113
|
+
"script_id",
|
|
114
|
+
suggestions=[
|
|
115
|
+
"Pass a script identifier (e.g. 'morning_routine')",
|
|
116
|
+
"Use ha_search_entities(domain_filter='script') to list scripts",
|
|
117
|
+
],
|
|
118
|
+
)
|
|
106
119
|
config_result = await self._client.get_script_config(script_id)
|
|
107
120
|
# Extract actual script config body and compute hash before category injection
|
|
108
121
|
actual_config = config_result.get("config", config_result)
|
|
@@ -412,6 +425,21 @@ class ConfigScriptTools:
|
|
|
412
425
|
"""
|
|
413
426
|
bp_warnings: list[str] = []
|
|
414
427
|
try:
|
|
428
|
+
# ``script_id`` is required (always non-None). Reject empty/
|
|
429
|
+
# whitespace up-front so the caller gets a structured parameter
|
|
430
|
+
# error instead of a misleading ``RESOURCE_NOT_FOUND`` from
|
|
431
|
+
# the downstream upsert/fetch. Extension of the #1312
|
|
432
|
+
# validate_identifier_not_empty pattern to the scripts family
|
|
433
|
+
# per #1313.
|
|
434
|
+
validate_identifier_not_empty(
|
|
435
|
+
script_id,
|
|
436
|
+
"script_id",
|
|
437
|
+
suggestions=[
|
|
438
|
+
"Pass a script identifier (e.g. 'morning_routine')",
|
|
439
|
+
"Use ha_search_entities(domain_filter='script') to list scripts",
|
|
440
|
+
],
|
|
441
|
+
context={"action": "set"},
|
|
442
|
+
)
|
|
415
443
|
# Validate mutual exclusivity of config and python_transform
|
|
416
444
|
if config is not None and python_transform is not None:
|
|
417
445
|
raise_tool_error(
|
|
@@ -20,6 +20,7 @@ from pydantic import Field
|
|
|
20
20
|
from ..config import get_global_settings
|
|
21
21
|
from ..errors import ErrorCode, create_error_response
|
|
22
22
|
from .helpers import exception_to_structured_error, log_tool_usage, raise_tool_error
|
|
23
|
+
from .tools_config_dashboards import fetch_dashboards_list
|
|
23
24
|
from .tools_filesystem import (
|
|
24
25
|
MCP_TOOLS_DOMAIN,
|
|
25
26
|
_assert_mcp_tools_available,
|
|
@@ -45,9 +46,7 @@ async def _check_storage_mode_dashboard_collision(
|
|
|
45
46
|
return
|
|
46
47
|
url_path = yaml_path[len(_LOVELACE_DASHBOARD_PREFIX):]
|
|
47
48
|
try:
|
|
48
|
-
|
|
49
|
-
{"type": "lovelace/dashboards/list"}
|
|
50
|
-
)
|
|
49
|
+
dashboards = await fetch_dashboards_list(client)
|
|
51
50
|
except Exception as exc:
|
|
52
51
|
logger.warning(
|
|
53
52
|
"lovelace/dashboards/list WS query failed (%s); skipping collision check",
|
|
@@ -55,18 +54,6 @@ async def _check_storage_mode_dashboard_collision(
|
|
|
55
54
|
)
|
|
56
55
|
return
|
|
57
56
|
|
|
58
|
-
if isinstance(result, dict) and "result" in result:
|
|
59
|
-
dashboards = result["result"]
|
|
60
|
-
elif isinstance(result, list):
|
|
61
|
-
dashboards = result
|
|
62
|
-
else:
|
|
63
|
-
logger.warning(
|
|
64
|
-
"lovelace/dashboards/list returned unexpected shape (%s); "
|
|
65
|
-
"skipping collision check",
|
|
66
|
-
type(result).__name__,
|
|
67
|
-
)
|
|
68
|
-
return
|
|
69
|
-
|
|
70
57
|
for entry in dashboards or []:
|
|
71
58
|
if (
|
|
72
59
|
isinstance(entry, dict)
|
|
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.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/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.dev531 → ha_mcp_dev-7.5.0.dev533}/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
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/tools/tools_config_helpers.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
|
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/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.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/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.dev531 → ha_mcp_dev-7.5.0.dev533}/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.dev531 → ha_mcp_dev-7.5.0.dev533}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.5.0.dev531 → ha_mcp_dev-7.5.0.dev533}/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
|