ha-mcp-dev 7.6.0.dev615__tar.gz → 7.6.0.dev616__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.6.0.dev615/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev616}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/pyproject.toml +1 -1
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/smart_search.py +151 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_entities.py +7 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_integrations.py +109 -62
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_search.py +7 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/setup.cfg +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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.6.0.
|
|
7
|
+
version = "7.6.0.dev616"
|
|
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"
|
|
@@ -6,6 +6,7 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
import os
|
|
8
8
|
import random
|
|
9
|
+
import re
|
|
9
10
|
import time
|
|
10
11
|
from typing import Any
|
|
11
12
|
|
|
@@ -23,6 +24,8 @@ from ..utils.fuzzy_search import (
|
|
|
23
24
|
)
|
|
24
25
|
from .helpers import exception_to_structured_error, safe_info, safe_progress
|
|
25
26
|
from .tools_config_dashboards import fetch_dashboards_list
|
|
27
|
+
from .tools_config_entry_flow import FLOW_HELPER_TYPES
|
|
28
|
+
from .tools_integrations import fetch_entry_options
|
|
26
29
|
|
|
27
30
|
logger = logging.getLogger(__name__)
|
|
28
31
|
|
|
@@ -1755,6 +1758,22 @@ class SmartSearchTools:
|
|
|
1755
1758
|
elif isinstance(result, Exception):
|
|
1756
1759
|
logger.debug(f"Helper list fetch failed: {result}")
|
|
1757
1760
|
|
|
1761
|
+
# Flow-based helpers (template, group, utility_meter,
|
|
1762
|
+
# derivative, ...) are config entries, not storage records,
|
|
1763
|
+
# and have no `<type>/list` WebSocket endpoint. Pull them via
|
|
1764
|
+
# the standard /config/config_entries/entry REST surface and
|
|
1765
|
+
# probe each entry's options flow so the helper's current
|
|
1766
|
+
# config (template body, group members, source entity, ...)
|
|
1767
|
+
# is searchable alongside the input_* helpers above.
|
|
1768
|
+
results["helpers"].extend(
|
|
1769
|
+
await self._search_flow_helpers(
|
|
1770
|
+
query_lower,
|
|
1771
|
+
exact_match,
|
|
1772
|
+
semaphore,
|
|
1773
|
+
include_config=include_config,
|
|
1774
|
+
)
|
|
1775
|
+
)
|
|
1776
|
+
|
|
1758
1777
|
phase_done += 1
|
|
1759
1778
|
await safe_progress(
|
|
1760
1779
|
ctx,
|
|
@@ -1969,6 +1988,138 @@ class SmartSearchTools:
|
|
|
1969
1988
|
},
|
|
1970
1989
|
)
|
|
1971
1990
|
|
|
1991
|
+
async def _search_flow_helpers(
|
|
1992
|
+
self,
|
|
1993
|
+
query_lower: str,
|
|
1994
|
+
exact_match: bool,
|
|
1995
|
+
semaphore: asyncio.Semaphore,
|
|
1996
|
+
*,
|
|
1997
|
+
include_config: bool,
|
|
1998
|
+
) -> list[dict[str, Any]]:
|
|
1999
|
+
"""Search UI-created flow-based helpers (template, group, …).
|
|
2000
|
+
|
|
2001
|
+
Flow-helpers live as config entries (not storage records) and have
|
|
2002
|
+
no ``<type>/list`` endpoint. Lists them via the standard config
|
|
2003
|
+
entries REST endpoint, then probes each entry's options flow so the
|
|
2004
|
+
helper's current config — template body, group members, source
|
|
2005
|
+
entity, etc. — is searchable.
|
|
2006
|
+
|
|
2007
|
+
Cost: 1 REST call + one options-flow probe per flow-helper config
|
|
2008
|
+
entry, parallelised under ``semaphore``. The probe is skipped when
|
|
2009
|
+
the title alone already scores the maximum (a deeper config match can
|
|
2010
|
+
only raise the total, never lower it); any title that leaves headroom
|
|
2011
|
+
is still probed for accurate scoring and ``match_in_config``.
|
|
2012
|
+
"""
|
|
2013
|
+
try:
|
|
2014
|
+
response = await self.client._request("GET", "/config/config_entries/entry")
|
|
2015
|
+
except Exception as exc:
|
|
2016
|
+
logger.debug(f"flow-helper search: list_entries failed: {exc}")
|
|
2017
|
+
return []
|
|
2018
|
+
|
|
2019
|
+
if not isinstance(response, list):
|
|
2020
|
+
return []
|
|
2021
|
+
|
|
2022
|
+
flow_entries = [
|
|
2023
|
+
e
|
|
2024
|
+
for e in response
|
|
2025
|
+
if isinstance(e, dict)
|
|
2026
|
+
and e.get("domain") in FLOW_HELPER_TYPES
|
|
2027
|
+
and e.get("supports_options")
|
|
2028
|
+
]
|
|
2029
|
+
if not flow_entries:
|
|
2030
|
+
return []
|
|
2031
|
+
|
|
2032
|
+
async def score_entry(entry: dict[str, Any]) -> dict[str, Any] | None:
|
|
2033
|
+
entry_id = entry.get("entry_id")
|
|
2034
|
+
if not isinstance(entry_id, str):
|
|
2035
|
+
return None
|
|
2036
|
+
domain = entry.get("domain", "")
|
|
2037
|
+
title = entry.get("title") or entry_id
|
|
2038
|
+
|
|
2039
|
+
# Score the name against a title-derived slug, never the opaque
|
|
2040
|
+
# config-entry ULID: a random ULID substring would otherwise
|
|
2041
|
+
# produce false-positive name matches (e.g. a 3-char query that
|
|
2042
|
+
# happens to occur inside the base32 id). The slug mirrors the
|
|
2043
|
+
# storage-helper path, which scores a name-derived id rather than
|
|
2044
|
+
# an opaque key. entry_id is still returned to the caller; it just
|
|
2045
|
+
# isn't a search target.
|
|
2046
|
+
title_slug = re.sub(r"[^a-z0-9]+", "_", title.lower()).strip("_")
|
|
2047
|
+
title_pseudo_eid = f"{domain}.{title_slug}" if title_slug else domain
|
|
2048
|
+
name_score = self.fuzzy_searcher._calculate_entity_score(
|
|
2049
|
+
title_pseudo_eid, title, domain, query_lower
|
|
2050
|
+
)
|
|
2051
|
+
|
|
2052
|
+
options: dict[str, Any] = {}
|
|
2053
|
+
# Only a perfect title match (score 100) makes the deeper options
|
|
2054
|
+
# probe redundant — the probe can only raise the total, never lower
|
|
2055
|
+
# it, so anything below 100 is worth probing (in both exact and
|
|
2056
|
+
# fuzzy modes) for accurate scoring and ``match_in_config``.
|
|
2057
|
+
need_probe = include_config or (
|
|
2058
|
+
self._score_deep_match(
|
|
2059
|
+
title_pseudo_eid,
|
|
2060
|
+
title,
|
|
2061
|
+
name_score,
|
|
2062
|
+
0,
|
|
2063
|
+
query_lower,
|
|
2064
|
+
exact_match,
|
|
2065
|
+
)[0]
|
|
2066
|
+
< 100
|
|
2067
|
+
)
|
|
2068
|
+
if need_probe:
|
|
2069
|
+
async with semaphore:
|
|
2070
|
+
options = await fetch_entry_options(
|
|
2071
|
+
self.client, entry_id, quiet=True
|
|
2072
|
+
)
|
|
2073
|
+
|
|
2074
|
+
# Search the title, domain, and probed options — but not the opaque
|
|
2075
|
+
# entry_id (it would match random ULID substrings; it is returned
|
|
2076
|
+
# in the result for the caller regardless).
|
|
2077
|
+
haystack: dict[str, Any] = {
|
|
2078
|
+
"title": title,
|
|
2079
|
+
"domain": domain,
|
|
2080
|
+
"options": options,
|
|
2081
|
+
}
|
|
2082
|
+
config_score = self._search_in_dict(haystack, query_lower, exact_match)
|
|
2083
|
+
total_score, threshold, match_in_name = self._score_deep_match(
|
|
2084
|
+
title_pseudo_eid,
|
|
2085
|
+
title,
|
|
2086
|
+
name_score,
|
|
2087
|
+
config_score,
|
|
2088
|
+
query_lower,
|
|
2089
|
+
exact_match,
|
|
2090
|
+
)
|
|
2091
|
+
if total_score < threshold:
|
|
2092
|
+
return None
|
|
2093
|
+
|
|
2094
|
+
result: dict[str, Any] = {
|
|
2095
|
+
"entry_id": entry_id,
|
|
2096
|
+
"helper_type": domain,
|
|
2097
|
+
"name": title,
|
|
2098
|
+
"score": total_score,
|
|
2099
|
+
"match_in_name": match_in_name,
|
|
2100
|
+
"match_in_config": config_score >= threshold,
|
|
2101
|
+
}
|
|
2102
|
+
if include_config:
|
|
2103
|
+
result["config"] = options
|
|
2104
|
+
return result
|
|
2105
|
+
|
|
2106
|
+
scored = await asyncio.gather(
|
|
2107
|
+
*(score_entry(e) for e in flow_entries),
|
|
2108
|
+
return_exceptions=True,
|
|
2109
|
+
)
|
|
2110
|
+
out: list[dict[str, Any]] = []
|
|
2111
|
+
for item in scored:
|
|
2112
|
+
if isinstance(item, dict):
|
|
2113
|
+
out.append(item)
|
|
2114
|
+
elif isinstance(item, Exception):
|
|
2115
|
+
# The probe swallows its own transient/API errors, so anything
|
|
2116
|
+
# reaching here is a scoring/extraction bug (e.g. a shape
|
|
2117
|
+
# assumption breaking on a future HA version). Log at warning so
|
|
2118
|
+
# it's discoverable — one bad entry must not sink the whole
|
|
2119
|
+
# multi-source deep_search, so we drop it and keep going.
|
|
2120
|
+
logger.warning(f"flow-helper scoring failed: {item!r}")
|
|
2121
|
+
return out
|
|
2122
|
+
|
|
1972
2123
|
def _score_deep_match(
|
|
1973
2124
|
self,
|
|
1974
2125
|
entity_id: str,
|
|
@@ -1189,6 +1189,12 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1189
1189
|
via the ha_set_entity(expose_to=...) parameter, not the options dict.
|
|
1190
1190
|
- platform: Integration platform (e.g., "hue", "zwave_js")
|
|
1191
1191
|
- device_id: Associated device ID (null if standalone)
|
|
1192
|
+
- config_entry_id: Parent config entry's ID (null for YAML-only
|
|
1193
|
+
entities). When non-null — e.g. for UI-created template/group/
|
|
1194
|
+
utility_meter/derivative/... helpers — pass it to
|
|
1195
|
+
``ha_get_integration(entry_id=..., include_options=True)`` to read the
|
|
1196
|
+
helper's current config (template body, group members, etc.) without
|
|
1197
|
+
scanning a domain list.
|
|
1192
1198
|
- unique_id: Integration's unique identifier
|
|
1193
1199
|
"""
|
|
1194
1200
|
try:
|
|
@@ -1254,6 +1260,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1254
1260
|
"options": entry.get("options", {}),
|
|
1255
1261
|
"platform": entry.get("platform"),
|
|
1256
1262
|
"device_id": entry.get("device_id"),
|
|
1263
|
+
"config_entry_id": entry.get("config_entry_id"),
|
|
1257
1264
|
"unique_id": entry.get("unique_id"),
|
|
1258
1265
|
}
|
|
1259
1266
|
|
|
@@ -101,6 +101,98 @@ assert set(get_args(HelperTypeLiteral)) == (
|
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def options_from_form_flow(flow: dict[str, Any]) -> dict[str, Any]:
|
|
105
|
+
"""Extract ``{field_name: current_value}`` from a form-type OptionsFlow.
|
|
106
|
+
|
|
107
|
+
Reads each ``data_schema`` entry's ``default`` key, falling back to
|
|
108
|
+
``value`` (constant-type fields ship ``value`` instead of ``default``)
|
|
109
|
+
and then ``description.suggested_value`` (UI-created template, group,
|
|
110
|
+
utility_meter, and other flow-based helpers stash the current value
|
|
111
|
+
there — voluptuous renders ``suggested_value=...`` into the
|
|
112
|
+
``description`` sub-object, not as a top-level field key). Fields with
|
|
113
|
+
a missing or ``None`` value are skipped.
|
|
114
|
+
"""
|
|
115
|
+
out: dict[str, Any] = {}
|
|
116
|
+
# Defensive: HA should always return a list of dict fields, but guard
|
|
117
|
+
# against malformed shapes so a bad response degrades to {} instead of
|
|
118
|
+
# raising AttributeError (e.g. a string data_schema would iterate chars).
|
|
119
|
+
data_schema = flow.get("data_schema")
|
|
120
|
+
if not isinstance(data_schema, list):
|
|
121
|
+
return out
|
|
122
|
+
for field in data_schema:
|
|
123
|
+
if not isinstance(field, dict):
|
|
124
|
+
continue
|
|
125
|
+
name = field.get("name")
|
|
126
|
+
if name is None:
|
|
127
|
+
continue
|
|
128
|
+
value = field.get("default", field.get("value"))
|
|
129
|
+
if value is None:
|
|
130
|
+
description = field.get("description")
|
|
131
|
+
if isinstance(description, dict):
|
|
132
|
+
value = description.get("suggested_value")
|
|
133
|
+
if value is not None:
|
|
134
|
+
out[name] = value
|
|
135
|
+
return out
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async def fetch_entry_options(
|
|
139
|
+
client: Any, entry_id: str, *, quiet: bool = False
|
|
140
|
+
) -> dict[str, Any]:
|
|
141
|
+
"""Read the current ``options`` for a config entry via its OptionsFlow.
|
|
142
|
+
|
|
143
|
+
Home Assistant does not expose ``ConfigEntry.options`` through any
|
|
144
|
+
read-only REST or WebSocket endpoint — ``/api/config/config_entries/entry``
|
|
145
|
+
deliberately omits the field. The closest approximation that the HA UI
|
|
146
|
+
itself uses is the ``default`` values populated into the OptionsFlow's
|
|
147
|
+
first-step ``data_schema``: integrations build that schema from the
|
|
148
|
+
existing options dict, so the defaults match the persisted state.
|
|
149
|
+
|
|
150
|
+
Starts the flow, harvests ``{name: default}`` from the first step, and
|
|
151
|
+
aborts the flow in ``finally`` so it doesn't sit half-open.
|
|
152
|
+
|
|
153
|
+
Returns ``{}`` on any failure (unsupported entry, non-form first step
|
|
154
|
+
such as a menu, init/abort errors) so callers can treat the return as
|
|
155
|
+
the canonical "options" field without further checks.
|
|
156
|
+
|
|
157
|
+
Probe failures log at ``warning`` (so breakage of a deliberate
|
|
158
|
+
single-entry probe is discoverable) unless ``quiet=True``, which demotes
|
|
159
|
+
them to ``debug`` for bulk fan-out callers (e.g. ``smart_search`` probes
|
|
160
|
+
one entry per flow-helper on every ``ha_deep_search``; a per-entry
|
|
161
|
+
warning there would spam the log on routine searches).
|
|
162
|
+
|
|
163
|
+
Exposed at module level (not as a method) so non-class callers such as
|
|
164
|
+
``smart_search._search_flow_helpers`` can probe flow-helper config
|
|
165
|
+
without instantiating ``IntegrationTools``.
|
|
166
|
+
"""
|
|
167
|
+
log_probe_failure = logger.debug if quiet else logger.warning
|
|
168
|
+
flow_id: str | None = None
|
|
169
|
+
try:
|
|
170
|
+
flow = await client.start_options_flow(entry_id)
|
|
171
|
+
flow_id = flow.get("flow_id")
|
|
172
|
+
flow_type = flow.get("type")
|
|
173
|
+
if flow_type != "form":
|
|
174
|
+
log_probe_failure(
|
|
175
|
+
f"OptionsFlow for {entry_id} returned type={flow_type!r}, "
|
|
176
|
+
f"not a form — cannot extract option defaults"
|
|
177
|
+
)
|
|
178
|
+
return {}
|
|
179
|
+
return options_from_form_flow(flow)
|
|
180
|
+
except Exception as exc:
|
|
181
|
+
log_probe_failure(
|
|
182
|
+
f"Failed to fetch options for {entry_id}: {type(exc).__name__}: {exc}"
|
|
183
|
+
)
|
|
184
|
+
return {}
|
|
185
|
+
finally:
|
|
186
|
+
if flow_id:
|
|
187
|
+
try:
|
|
188
|
+
await client.abort_options_flow(flow_id)
|
|
189
|
+
except Exception as abort_err:
|
|
190
|
+
log_probe_failure(
|
|
191
|
+
f"Failed to abort options flow {flow_id}: "
|
|
192
|
+
f"{type(abort_err).__name__}: {abort_err}"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
104
196
|
async def _get_entry_id_for_flow_helper(
|
|
105
197
|
client: Any,
|
|
106
198
|
helper_type: str,
|
|
@@ -213,7 +305,13 @@ class IntegrationTools:
|
|
|
213
305
|
Field(
|
|
214
306
|
description="Include the options object for each entry. "
|
|
215
307
|
"Automatically enabled when domain filter is set. "
|
|
216
|
-
"
|
|
308
|
+
"For UI-created flow-based helpers (template, group, "
|
|
309
|
+
"utility_meter, derivative, ...), the current config — "
|
|
310
|
+
"template body, group members, source entity, etc. — is "
|
|
311
|
+
"surfaced here by probing the options flow. Prefer this over "
|
|
312
|
+
"include_schema when you only need to read the current values; "
|
|
313
|
+
"use include_schema when you also need the field types or "
|
|
314
|
+
"selector metadata.",
|
|
217
315
|
default=False,
|
|
218
316
|
),
|
|
219
317
|
] = False,
|
|
@@ -614,7 +712,7 @@ class IntegrationTools:
|
|
|
614
712
|
|
|
615
713
|
# Surface `options` on every per-entry response (HA's REST endpoint
|
|
616
714
|
# omits the field). For entries with supports_options=True we probe
|
|
617
|
-
# via OptionsFlow — see `
|
|
715
|
+
# via OptionsFlow — see `fetch_entry_options`. When include_schema
|
|
618
716
|
# is also requested, `_fetch_options_schema` below populates options
|
|
619
717
|
# from the same flow init so we don't pay for two round-trips.
|
|
620
718
|
if isinstance(result, dict):
|
|
@@ -758,68 +856,17 @@ class IntegrationTools:
|
|
|
758
856
|
|
|
759
857
|
@staticmethod
|
|
760
858
|
def _options_from_form_flow(flow: dict[str, Any]) -> dict[str, Any]:
|
|
761
|
-
"""
|
|
762
|
-
|
|
763
|
-
Reads each ``data_schema`` entry's ``default`` key, falling back to
|
|
764
|
-
``value`` only when the ``default`` key is absent (constant-type
|
|
765
|
-
fields ship ``value`` instead of ``default``). Fields with a missing
|
|
766
|
-
or ``None`` value are skipped.
|
|
767
|
-
"""
|
|
768
|
-
out: dict[str, Any] = {}
|
|
769
|
-
for field in flow.get("data_schema") or []:
|
|
770
|
-
name = field.get("name")
|
|
771
|
-
if name is None:
|
|
772
|
-
continue
|
|
773
|
-
value = field.get("default", field.get("value"))
|
|
774
|
-
if value is not None:
|
|
775
|
-
out[name] = value
|
|
776
|
-
return out
|
|
859
|
+
"""Class-method alias for :func:`options_from_form_flow`."""
|
|
860
|
+
return options_from_form_flow(flow)
|
|
777
861
|
|
|
778
862
|
async def _fetch_entry_options(self, entry_id: str) -> dict[str, Any]:
|
|
779
|
-
"""
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
itself uses is the ``default`` values populated into the OptionsFlow's
|
|
785
|
-
first-step ``data_schema``: integrations build that schema from the
|
|
786
|
-
existing options dict, so the defaults match the persisted state.
|
|
787
|
-
|
|
788
|
-
Starts the flow, harvests ``{name: default}`` from the first step,
|
|
789
|
-
and aborts the flow in ``finally`` so it doesn't sit half-open.
|
|
790
|
-
|
|
791
|
-
Returns ``{}`` on any failure (unsupported entry, non-form first step
|
|
792
|
-
such as a menu, init/abort errors) so callers can treat the return as
|
|
793
|
-
the canonical "options" field without further checks. Unexpected
|
|
794
|
-
exception types are logged at ``warning`` so probe breakage is
|
|
795
|
-
discoverable.
|
|
863
|
+
"""Instance wrapper around :func:`fetch_entry_options`.
|
|
864
|
+
|
|
865
|
+
Kept so existing call sites (and the ``include_schema`` path) read
|
|
866
|
+
naturally as ``self._fetch_entry_options(...)``; the probe logic and
|
|
867
|
+
full rationale live on the module-level function.
|
|
796
868
|
"""
|
|
797
|
-
|
|
798
|
-
try:
|
|
799
|
-
flow = await self._client.start_options_flow(entry_id)
|
|
800
|
-
flow_id = flow.get("flow_id")
|
|
801
|
-
flow_type = flow.get("type")
|
|
802
|
-
if flow_type != "form":
|
|
803
|
-
logger.debug(
|
|
804
|
-
f"OptionsFlow for {entry_id} returned type={flow_type!r}, "
|
|
805
|
-
f"not a form — cannot extract option defaults"
|
|
806
|
-
)
|
|
807
|
-
return {}
|
|
808
|
-
return self._options_from_form_flow(flow)
|
|
809
|
-
except Exception as exc:
|
|
810
|
-
logger.warning(
|
|
811
|
-
f"Failed to fetch options for {entry_id}: {type(exc).__name__}: {exc}"
|
|
812
|
-
)
|
|
813
|
-
return {}
|
|
814
|
-
finally:
|
|
815
|
-
if flow_id:
|
|
816
|
-
try:
|
|
817
|
-
await self._client.abort_options_flow(flow_id)
|
|
818
|
-
except Exception as abort_err:
|
|
819
|
-
logger.warning(
|
|
820
|
-
f"Failed to abort options flow {flow_id}: "
|
|
821
|
-
f"{type(abort_err).__name__}: {abort_err}"
|
|
822
|
-
)
|
|
869
|
+
return await fetch_entry_options(self._client, entry_id)
|
|
823
870
|
|
|
824
871
|
async def _fetch_options_schema(self, entry_id: str, resp: dict[str, Any]) -> None:
|
|
825
872
|
"""Start an options flow to read the schema, then abort it.
|
|
@@ -899,7 +946,7 @@ class IntegrationTools:
|
|
|
899
946
|
|
|
900
947
|
# `_format_entry` is sync and cannot probe the OptionsFlow; options
|
|
901
948
|
# are filled in by a second async pass below for entries that
|
|
902
|
-
# advertise supports_options=True. See `
|
|
949
|
+
# advertise supports_options=True. See `fetch_entry_options`.
|
|
903
950
|
formatted_entries = [
|
|
904
951
|
self._format_entry(entry, include_opts, logger_levels) for entry in entries
|
|
905
952
|
]
|
|
@@ -1473,6 +1473,13 @@ def register_search_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
1473
1473
|
**NOTE:** Dashboards and badges are NOT searched by default. Add 'dashboard' to
|
|
1474
1474
|
search_types to include them.
|
|
1475
1475
|
|
|
1476
|
+
The 'helper' search covers both input_* helpers (input_boolean, input_number, ...)
|
|
1477
|
+
and UI-created flow-based helpers (template, group, utility_meter, derivative, ...).
|
|
1478
|
+
For flow-helpers, results carry the parent config entry id under ``entry_id``.
|
|
1479
|
+
When ``include_config=False`` (the default), pair with
|
|
1480
|
+
``ha_get_integration(entry_id=..., include_options=True)`` to retrieve the full
|
|
1481
|
+
config; set ``include_config=True`` to get it inline in one call.
|
|
1482
|
+
|
|
1476
1483
|
Args:
|
|
1477
1484
|
query: Search query (exact substring by default, or fuzzy with exact_match=False)
|
|
1478
1485
|
search_types: Types to search (default: ["automation", "script", "scene", "helper"])
|
|
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
|
|
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.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_entry_flow.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.6.0.dev615 → ha_mcp_dev-7.6.0.dev616}/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
|