ha-mcp-dev 7.3.0.dev398__tar.gz → 7.3.0.dev400__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.3.0.dev398/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.3.0.dev400}/PKG-INFO +1 -1
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/pyproject.toml +1 -1
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/__init__.py +3 -1
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/__main__.py +24 -2
- ha_mcp_dev-7.3.0.dev400/src/ha_mcp/_version.py +58 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/config.py +4 -6
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_areas.py +8 -1
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_automations.py +54 -7
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_helpers.py +3 -3
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_scripts.py +8 -7
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/util_helpers.py +7 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/transforms/categorized_search.py +41 -4
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/LICENSE +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/README.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/setup.cfg +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/smart_search.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/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.3.0.
|
|
7
|
+
version = "7.3.0.dev400"
|
|
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"
|
|
@@ -5,7 +5,9 @@ A Model Context Protocol server that provides complete control over Home Assista
|
|
|
5
5
|
through REST API and WebSocket integration with 20+ enhanced tools.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
from ._version import get_version
|
|
9
|
+
|
|
10
|
+
__version__ = get_version()
|
|
9
11
|
__author__ = "Julien"
|
|
10
12
|
__license__ = "MIT"
|
|
11
13
|
|
|
@@ -283,6 +283,7 @@ def _setup_standard_mode() -> None:
|
|
|
283
283
|
settings = get_settings()
|
|
284
284
|
_validate_standard_credentials(settings)
|
|
285
285
|
_setup_logging(settings.log_level)
|
|
286
|
+
_log_startup_version()
|
|
286
287
|
|
|
287
288
|
|
|
288
289
|
def _http_run_kwargs(transport: str, port: int, path: str) -> dict:
|
|
@@ -411,6 +412,25 @@ def _setup_logging(log_level_str: str, force: bool = False) -> None:
|
|
|
411
412
|
logging.getLogger("fastmcp.server.server").addFilter(ToolValidationLogFilter())
|
|
412
413
|
|
|
413
414
|
|
|
415
|
+
def _log_startup_version() -> None:
|
|
416
|
+
"""Log ha-mcp version at startup, plus a dev-channel banner when relevant.
|
|
417
|
+
|
|
418
|
+
The dev banner only fires for standalone dev installs (Docker ``:dev`` /
|
|
419
|
+
``:latest``, or ``pip install ha-mcp-dev``). It is suppressed under the HA
|
|
420
|
+
Supervisor because add-on users already pick dev vs stable in the HAOS UI.
|
|
421
|
+
"""
|
|
422
|
+
from ha_mcp._version import get_version, is_dev_version, is_running_in_addon
|
|
423
|
+
|
|
424
|
+
version = get_version()
|
|
425
|
+
logger.info(f"ha-mcp {version}")
|
|
426
|
+
if is_dev_version(version) and not is_running_in_addon():
|
|
427
|
+
logger.warning(
|
|
428
|
+
"This is the dev channel. For the stable release use the "
|
|
429
|
+
"'ghcr.io/homeassistant-ai/ha-mcp:stable' Docker tag "
|
|
430
|
+
"(or 'pip install ha-mcp' on PyPI)."
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
414
434
|
def _get_timestamped_uvicorn_log_config() -> dict:
|
|
415
435
|
"""Return a Uvicorn log config with human-readable timestamps added."""
|
|
416
436
|
from uvicorn.config import LOGGING_CONFIG
|
|
@@ -580,9 +600,9 @@ def main() -> None:
|
|
|
580
600
|
"""Run server via CLI using FastMCP's stdio transport."""
|
|
581
601
|
# Handle --version flag early, before server creation requires config
|
|
582
602
|
if "--version" in sys.argv or "-V" in sys.argv:
|
|
583
|
-
from
|
|
603
|
+
from ha_mcp._version import get_version
|
|
584
604
|
|
|
585
|
-
print(f"ha-mcp {
|
|
605
|
+
print(f"ha-mcp {get_version()}")
|
|
586
606
|
sys.exit(0)
|
|
587
607
|
|
|
588
608
|
# Check for smoke test flag
|
|
@@ -605,6 +625,7 @@ def main() -> None:
|
|
|
605
625
|
sys.exit(1)
|
|
606
626
|
|
|
607
627
|
_setup_logging(settings.log_level)
|
|
628
|
+
_log_startup_version()
|
|
608
629
|
|
|
609
630
|
_run_entrypoint(_run_with_graceful_shutdown(), "Server")
|
|
610
631
|
|
|
@@ -780,6 +801,7 @@ def main_oauth() -> None:
|
|
|
780
801
|
for logger_name in ["ha_mcp", "ha_mcp.auth", "ha_mcp.auth.provider"]:
|
|
781
802
|
logging.getLogger(logger_name).setLevel(getattr(logging, log_level))
|
|
782
803
|
logger.info(f"OAuth mode logging configured at {log_level} level")
|
|
804
|
+
_log_startup_version()
|
|
783
805
|
|
|
784
806
|
port, path = _get_http_runtime(default_port=8086)
|
|
785
807
|
base_url = os.getenv("MCP_BASE_URL")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Version resolution for the ha-mcp package.
|
|
2
|
+
|
|
3
|
+
Kept as a standalone module (no other ``ha_mcp`` imports) so it can be used from
|
|
4
|
+
``__init__.py`` and ``config.py`` without circular-import risk.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_version() -> str:
|
|
17
|
+
"""Return the installed ha-mcp version.
|
|
18
|
+
|
|
19
|
+
Resolution order:
|
|
20
|
+
1. ``HA_MCP_BUILD_VERSION`` env var — set by Docker/add-on builds that can't
|
|
21
|
+
rewrite ``pyproject.toml`` before install, so the dev suffix still reaches
|
|
22
|
+
the running process. Stable builds leave it unset.
|
|
23
|
+
2. ``ha-mcp`` package metadata — stable PyPI + stable Docker.
|
|
24
|
+
3. ``ha-mcp-dev`` package metadata — PyPI dev channel (renamed package).
|
|
25
|
+
|
|
26
|
+
If none of the above resolve, logs a warning and returns ``"unknown"``.
|
|
27
|
+
The "unknown" string is itself diagnostic in bug reports and startup logs
|
|
28
|
+
— it tells triagers the install didn't register package metadata (e.g. a
|
|
29
|
+
source checkout without ``pip install -e .``, or a broken Docker layer).
|
|
30
|
+
"""
|
|
31
|
+
if override := os.environ.get("HA_MCP_BUILD_VERSION"):
|
|
32
|
+
return override
|
|
33
|
+
for pkg_name in ("ha-mcp", "ha-mcp-dev"):
|
|
34
|
+
try:
|
|
35
|
+
return importlib.metadata.version(pkg_name)
|
|
36
|
+
except importlib.metadata.PackageNotFoundError:
|
|
37
|
+
continue
|
|
38
|
+
logger.warning(
|
|
39
|
+
"ha-mcp package metadata not found and HA_MCP_BUILD_VERSION unset — "
|
|
40
|
+
"version will be reported as 'unknown'. Reinstall the package or set "
|
|
41
|
+
"HA_MCP_BUILD_VERSION if this is an intentional source-tree run."
|
|
42
|
+
)
|
|
43
|
+
return "unknown"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_dev_version(version: str) -> bool:
|
|
47
|
+
"""Return True when the version string contains a PEP 440 ``.dev`` suffix."""
|
|
48
|
+
return ".dev" in version
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_running_in_addon() -> bool:
|
|
52
|
+
"""Return True when running inside a Home Assistant add-on container.
|
|
53
|
+
|
|
54
|
+
The HA Supervisor injects ``SUPERVISOR_TOKEN`` into every add-on's env.
|
|
55
|
+
Checked so the standalone-Docker ``:stable`` banner isn't shown to add-on
|
|
56
|
+
users, who already see the dev/stable distinction in the HAOS add-on UI.
|
|
57
|
+
"""
|
|
58
|
+
return bool(os.environ.get("SUPERVISOR_TOKEN"))
|
|
@@ -2,22 +2,20 @@
|
|
|
2
2
|
Configuration management for Home Assistant MCP Server.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
import importlib.metadata
|
|
6
5
|
import os
|
|
7
6
|
|
|
8
7
|
# Load environment variables from .env file with HAMCP_ENV_FILE support
|
|
9
8
|
# Use absolute path to ensure .env is found regardless of cwd
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
|
|
12
|
-
try:
|
|
13
|
-
_PACKAGE_VERSION = importlib.metadata.version("ha-mcp")
|
|
14
|
-
except importlib.metadata.PackageNotFoundError:
|
|
15
|
-
_PACKAGE_VERSION = "unknown"
|
|
16
|
-
|
|
17
11
|
from dotenv import load_dotenv
|
|
18
12
|
from pydantic import Field, field_validator, model_validator
|
|
19
13
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
20
14
|
|
|
15
|
+
from ha_mcp._version import get_version
|
|
16
|
+
|
|
17
|
+
_PACKAGE_VERSION = get_version()
|
|
18
|
+
|
|
21
19
|
project_root = Path(__file__).parent.parent.parent
|
|
22
20
|
|
|
23
21
|
# Demo environment token - use HOMEASSISTANT_TOKEN="demo" to connect to the public demo
|
|
@@ -222,8 +222,15 @@ class AreaTools:
|
|
|
222
222
|
"""
|
|
223
223
|
Create or update a Home Assistant area (room).
|
|
224
224
|
|
|
225
|
-
Provide name only to create a new area. Provide area_id to update existing.
|
|
226
225
|
Areas organize entities by physical location for room-based control.
|
|
226
|
+
|
|
227
|
+
Create: provide name only.
|
|
228
|
+
Update: provide area_id (from ha_config_list_areas) plus any fields to change.
|
|
229
|
+
|
|
230
|
+
EXAMPLES:
|
|
231
|
+
ha_config_set_area(name="Kitchen")
|
|
232
|
+
ha_config_set_area(name="Living Room", icon="mdi:sofa")
|
|
233
|
+
ha_config_set_area(area_id="kitchen", name="Kitchen Renamed", floor_id="ground_floor")
|
|
227
234
|
"""
|
|
228
235
|
try:
|
|
229
236
|
# Parse aliases if provided as string
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -41,6 +41,7 @@ from .reference_validator import validate_config_references
|
|
|
41
41
|
from .util_helpers import (
|
|
42
42
|
apply_entity_category,
|
|
43
43
|
coerce_bool_param,
|
|
44
|
+
coerce_to_list,
|
|
44
45
|
fetch_entity_category,
|
|
45
46
|
merge_validation_meta,
|
|
46
47
|
parse_json_param,
|
|
@@ -413,7 +414,7 @@ class AutomationConfigTools:
|
|
|
413
414
|
BASIC EXAMPLES:
|
|
414
415
|
|
|
415
416
|
Simple time-based automation:
|
|
416
|
-
ha_config_set_automation({
|
|
417
|
+
ha_config_set_automation(config={
|
|
417
418
|
"alias": "Morning Lights",
|
|
418
419
|
"description": "Turn on bedroom lights at 7 AM to help wake up",
|
|
419
420
|
"trigger": [{"platform": "time", "at": "07:00:00"}],
|
|
@@ -421,7 +422,7 @@ class AutomationConfigTools:
|
|
|
421
422
|
})
|
|
422
423
|
|
|
423
424
|
Motion-activated lighting with condition:
|
|
424
|
-
ha_config_set_automation({
|
|
425
|
+
ha_config_set_automation(config={
|
|
425
426
|
"alias": "Motion Light",
|
|
426
427
|
"trigger": [{"platform": "state", "entity_id": "binary_sensor.motion", "to": "on"}],
|
|
427
428
|
"condition": [{"condition": "sun", "after": "sunset"}],
|
|
@@ -449,7 +450,7 @@ class AutomationConfigTools:
|
|
|
449
450
|
BLUEPRINT AUTOMATION EXAMPLES:
|
|
450
451
|
|
|
451
452
|
Create automation from blueprint:
|
|
452
|
-
ha_config_set_automation({
|
|
453
|
+
ha_config_set_automation(config={
|
|
453
454
|
"alias": "Motion Light Kitchen",
|
|
454
455
|
"use_blueprint": {
|
|
455
456
|
"path": "homeassistant/motion_light.yaml",
|
|
@@ -757,10 +758,14 @@ class AutomationConfigTools:
|
|
|
757
758
|
try:
|
|
758
759
|
parsed_config = parse_json_param(config, "config")
|
|
759
760
|
except ValueError as e:
|
|
760
|
-
raise_tool_error(
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
761
|
+
raise_tool_error(create_error_response(
|
|
762
|
+
code=ErrorCode.VALIDATION_INVALID_JSON,
|
|
763
|
+
message=f"Invalid config parameter: {e}",
|
|
764
|
+
suggestions=[
|
|
765
|
+
"Pass 'config' as a dict, not a JSON string, to avoid escaping issues.",
|
|
766
|
+
"Check for JSON syntax errors: unquoted keys, trailing commas, or invalid escape sequences.",
|
|
767
|
+
],
|
|
768
|
+
context={"parameter": "config"},
|
|
764
769
|
))
|
|
765
770
|
|
|
766
771
|
if parsed_config is None or not isinstance(parsed_config, dict):
|
|
@@ -788,12 +793,54 @@ class AutomationConfigTools:
|
|
|
788
793
|
|
|
789
794
|
missing_fields = [f for f in required_fields if f not in config_dict]
|
|
790
795
|
if missing_fields:
|
|
796
|
+
# If the caller supplied a 'sequence' key, the config looks like a
|
|
797
|
+
# script — point them at ha_config_set_script instead of the generic
|
|
798
|
+
# missing-fields error.
|
|
799
|
+
if "sequence" in config_dict and (
|
|
800
|
+
"trigger" in missing_fields or "action" in missing_fields
|
|
801
|
+
):
|
|
802
|
+
context: dict[str, Any] = {"missing_fields": missing_fields}
|
|
803
|
+
if identifier:
|
|
804
|
+
context["identifier"] = identifier
|
|
805
|
+
raise_tool_error(create_error_response(
|
|
806
|
+
code=ErrorCode.CONFIG_MISSING_REQUIRED_FIELDS,
|
|
807
|
+
message=f"Missing required fields: {', '.join(missing_fields)}",
|
|
808
|
+
details=(
|
|
809
|
+
"Config contains 'sequence', which belongs to scripts. "
|
|
810
|
+
"Automations use 'trigger' and 'action'; scripts use 'sequence'."
|
|
811
|
+
),
|
|
812
|
+
suggestions=[
|
|
813
|
+
"Did you mean ha_config_set_script? Scripts use 'sequence' directly.",
|
|
814
|
+
"For an automation, replace 'sequence' with 'action' and add a 'trigger'.",
|
|
815
|
+
],
|
|
816
|
+
context=context,
|
|
817
|
+
))
|
|
791
818
|
raise_tool_error(create_config_error(
|
|
792
819
|
f"Missing required fields: {', '.join(missing_fields)}",
|
|
793
820
|
identifier=identifier,
|
|
794
821
|
missing_fields=missing_fields,
|
|
795
822
|
))
|
|
796
823
|
|
|
824
|
+
# HA accepts conditions with 'platform' (trigger syntax) but then crashes
|
|
825
|
+
# with an unhelpful 500 rather than a 400 validation error.
|
|
826
|
+
for idx, cond in enumerate(coerce_to_list(config_dict.get("condition"))):
|
|
827
|
+
if not isinstance(cond, dict):
|
|
828
|
+
continue
|
|
829
|
+
if "platform" in cond and "condition" not in cond:
|
|
830
|
+
raise_tool_error(create_error_response(
|
|
831
|
+
code=ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
832
|
+
message=(
|
|
833
|
+
f"Condition at index {idx} uses 'platform' (trigger syntax). "
|
|
834
|
+
"Conditions use 'condition', not 'platform'."
|
|
835
|
+
),
|
|
836
|
+
suggestions=[
|
|
837
|
+
f"Replace 'platform' with 'condition': "
|
|
838
|
+
f"{{'condition': '{cond['platform']}', ...}}",
|
|
839
|
+
"Triggers use 'platform'; conditions use 'condition'.",
|
|
840
|
+
],
|
|
841
|
+
context={"condition_index": idx, "found_key": "platform"},
|
|
842
|
+
))
|
|
843
|
+
|
|
797
844
|
# Prevent duplicate creation when config contains an existing automation id
|
|
798
845
|
if identifier is None and "id" in config_dict:
|
|
799
846
|
existing_id = config_dict["id"]
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
@@ -801,16 +801,16 @@ def register_config_helper_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
|
|
801
801
|
|
|
802
802
|
EXAMPLES (menu-based types + tod, where first-call payload is non-obvious):
|
|
803
803
|
- template sensor:
|
|
804
|
-
ha_config_set_helper("template", "Room Temp",
|
|
804
|
+
ha_config_set_helper(helper_type="template", name="Room Temp",
|
|
805
805
|
config={"next_step_id": "sensor",
|
|
806
806
|
"state": "{{ states('sensor.x')|float }}",
|
|
807
807
|
"unit_of_measurement": "°C"})
|
|
808
808
|
- group (light):
|
|
809
|
-
ha_config_set_helper("group", "Kitchen Lights",
|
|
809
|
+
ha_config_set_helper(helper_type="group", name="Kitchen Lights",
|
|
810
810
|
config={"group_type": "light",
|
|
811
811
|
"entities": ["light.a", "light.b"]})
|
|
812
812
|
- tod (time-of-day indicator, cross-midnight OK):
|
|
813
|
-
ha_config_set_helper("tod", "Quiet Hours",
|
|
813
|
+
ha_config_set_helper(helper_type="tod", name="Quiet Hours",
|
|
814
814
|
config={"after_time": "22:00:00", "before_time": "07:00:00"})
|
|
815
815
|
|
|
816
816
|
For complex schemas and per-type parameter details, use ha_get_helper_schema.
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
@@ -313,19 +313,20 @@ class ConfigScriptTools:
|
|
|
313
313
|
- max: Maximum concurrent executions (for queued/parallel modes)
|
|
314
314
|
- fields: Input parameters for the script
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
SCRIPTS vs AUTOMATIONS: Scripts use 'sequence', NOT 'trigger' or 'action'.
|
|
317
|
+
If you need trigger-based execution, use ha_config_set_automation instead.
|
|
317
318
|
|
|
318
319
|
EXAMPLES:
|
|
319
320
|
|
|
320
321
|
Create basic delay script:
|
|
321
|
-
ha_config_set_script("wait_script", {
|
|
322
|
+
ha_config_set_script(script_id="wait_script", config={
|
|
322
323
|
"sequence": [{"delay": {"seconds": 5}}],
|
|
323
324
|
"alias": "Wait 5 Seconds",
|
|
324
325
|
"description": "Simple delay script"
|
|
325
326
|
})
|
|
326
327
|
|
|
327
328
|
Create service call script:
|
|
328
|
-
ha_config_set_script("blink_light", {
|
|
329
|
+
ha_config_set_script(script_id="blink_light", config={
|
|
329
330
|
"sequence": [
|
|
330
331
|
{"service": "light.turn_on", "target": {"entity_id": "light.living_room"}},
|
|
331
332
|
{"delay": {"seconds": 2}},
|
|
@@ -336,7 +337,7 @@ class ConfigScriptTools:
|
|
|
336
337
|
})
|
|
337
338
|
|
|
338
339
|
Create script with parameters:
|
|
339
|
-
ha_config_set_script("backup_script", {
|
|
340
|
+
ha_config_set_script(script_id="backup_script", config={
|
|
340
341
|
"alias": "Backup with Reference",
|
|
341
342
|
"description": "Create backup with optional reference parameter",
|
|
342
343
|
"fields": {
|
|
@@ -360,7 +361,7 @@ class ConfigScriptTools:
|
|
|
360
361
|
})
|
|
361
362
|
|
|
362
363
|
Update script:
|
|
363
|
-
ha_config_set_script("morning_routine", {
|
|
364
|
+
ha_config_set_script(script_id="morning_routine", config={
|
|
364
365
|
"sequence": [
|
|
365
366
|
{"service": "light.turn_on", "target": {"area_id": "bedroom"}},
|
|
366
367
|
{"service": "climate.set_temperature", "target": {"entity_id": "climate.bedroom"}, "data": {"temperature": 22}}
|
|
@@ -369,7 +370,7 @@ class ConfigScriptTools:
|
|
|
369
370
|
})
|
|
370
371
|
|
|
371
372
|
Create blueprint-based script:
|
|
372
|
-
ha_config_set_script("notification_script", {
|
|
373
|
+
ha_config_set_script(script_id="notification_script", config={
|
|
373
374
|
"alias": "My Notification Script",
|
|
374
375
|
"use_blueprint": {
|
|
375
376
|
"path": "notification_script.yaml",
|
|
@@ -381,7 +382,7 @@ class ConfigScriptTools:
|
|
|
381
382
|
})
|
|
382
383
|
|
|
383
384
|
Update blueprint script inputs:
|
|
384
|
-
ha_config_set_script("notification_script", {
|
|
385
|
+
ha_config_set_script(script_id="notification_script", config={
|
|
385
386
|
"alias": "My Notification Script",
|
|
386
387
|
"use_blueprint": {
|
|
387
388
|
"path": "notification_script.yaml",
|
|
@@ -532,6 +532,13 @@ async def apply_entity_category(
|
|
|
532
532
|
)
|
|
533
533
|
|
|
534
534
|
|
|
535
|
+
def coerce_to_list(value: Any) -> list[Any]:
|
|
536
|
+
"""Return value as a list: list → as-is, dict/other → [value], None/falsy → []."""
|
|
537
|
+
if isinstance(value, list):
|
|
538
|
+
return value
|
|
539
|
+
return [value] if value else []
|
|
540
|
+
|
|
541
|
+
|
|
535
542
|
def merge_validation_meta(
|
|
536
543
|
result: dict[str, Any], validation_meta: dict[str, Any]
|
|
537
544
|
) -> None:
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
@@ -111,19 +111,22 @@ def _build_proxy_descriptions(search_tool_name: str) -> dict[str, str]:
|
|
|
111
111
|
"read": (
|
|
112
112
|
f"Execute a read-only tool discovered via {search_tool_name}. "
|
|
113
113
|
f"Safe — does not modify any data or state.\n"
|
|
114
|
-
f"{_PROXY_PARAMS_SUFFIX}"
|
|
114
|
+
f"{_PROXY_PARAMS_SUFFIX}\n"
|
|
115
|
+
f'EXAMPLE: ha_call_read_tool(name="ha_get_history", arguments={{"entity_ids": "light.x", "start_time": "24h"}})'
|
|
115
116
|
),
|
|
116
117
|
"write": (
|
|
117
118
|
f"Execute a write tool discovered via {search_tool_name}. "
|
|
118
119
|
f"Creates or updates data. Use for any tool that modifies "
|
|
119
120
|
f"state but does not delete/remove resources.\n"
|
|
120
|
-
f"{_PROXY_PARAMS_SUFFIX}"
|
|
121
|
+
f"{_PROXY_PARAMS_SUFFIX}\n"
|
|
122
|
+
f'EXAMPLE: ha_call_write_tool(name="ha_config_set_area", arguments={{"name": "Kitchen"}})'
|
|
121
123
|
),
|
|
122
124
|
"delete": (
|
|
123
125
|
f"Execute a delete/remove tool discovered via {search_tool_name}. "
|
|
124
126
|
f"Permanently removes data. Use for tools that delete or "
|
|
125
127
|
f"remove resources (areas, automations, devices, etc.).\n"
|
|
126
|
-
f"{_PROXY_PARAMS_SUFFIX}"
|
|
128
|
+
f"{_PROXY_PARAMS_SUFFIX}\n"
|
|
129
|
+
f'EXAMPLE: ha_call_delete_tool(name="ha_config_remove_area", arguments={{"area_id": "old_area"}})'
|
|
127
130
|
),
|
|
128
131
|
}
|
|
129
132
|
|
|
@@ -254,13 +257,47 @@ class CategorizedSearchTransform(BM25SearchTransform):
|
|
|
254
257
|
async def categorized_call(
|
|
255
258
|
name: Annotated[str, "The name of the tool to call"],
|
|
256
259
|
arguments: Annotated[
|
|
257
|
-
dict[str, Any] | None, "Arguments to pass to the tool"
|
|
260
|
+
dict[str, Any] | str | None, "Arguments to pass to the tool"
|
|
258
261
|
] = None,
|
|
259
262
|
ctx: Context = None, # type: ignore[assignment]
|
|
260
263
|
) -> Any:
|
|
261
264
|
# Rebuild category cache if catalog has changed
|
|
262
265
|
await transform._rebuild_category_cache(ctx)
|
|
263
266
|
|
|
267
|
+
# Tolerate `arguments` passed as a JSON string — small models
|
|
268
|
+
# sometimes serialize it before sending. Parse once up front so
|
|
269
|
+
# downstream logic can assume a dict (or None).
|
|
270
|
+
if isinstance(arguments, str):
|
|
271
|
+
try:
|
|
272
|
+
parsed = json.loads(arguments)
|
|
273
|
+
except json.JSONDecodeError as e:
|
|
274
|
+
raise ToolError(json.dumps(create_error_response(
|
|
275
|
+
code=ErrorCode.VALIDATION_INVALID_JSON,
|
|
276
|
+
message=f"'arguments' is a string but not valid JSON: {e}",
|
|
277
|
+
suggestions=[
|
|
278
|
+
"Pass 'arguments' as an object, not a JSON string.",
|
|
279
|
+
],
|
|
280
|
+
context={"proxy_used": proxy_name, "tool_name": name},
|
|
281
|
+
))) from e
|
|
282
|
+
if not isinstance(parsed, dict):
|
|
283
|
+
raise ToolError(json.dumps(create_error_response(
|
|
284
|
+
code=ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
285
|
+
message=(
|
|
286
|
+
"'arguments' must be a JSON object "
|
|
287
|
+
f"(got {type(parsed).__name__})."
|
|
288
|
+
),
|
|
289
|
+
suggestions=[
|
|
290
|
+
"Pass 'arguments' as an object (dict), not a list or scalar.",
|
|
291
|
+
],
|
|
292
|
+
context={"proxy_used": proxy_name, "tool_name": name},
|
|
293
|
+
)))
|
|
294
|
+
logger.warning(
|
|
295
|
+
"Proxy %s received 'arguments' as a JSON string for tool %s — parsed as fallback",
|
|
296
|
+
proxy_name,
|
|
297
|
+
name,
|
|
298
|
+
)
|
|
299
|
+
arguments = parsed
|
|
300
|
+
|
|
264
301
|
# Determine which category set to check
|
|
265
302
|
if category == "read":
|
|
266
303
|
allowed = transform._read_tools
|
|
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.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/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
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/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
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_config_entry_flow.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.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp/tools/tools_voice_assistant.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.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.3.0.dev398 → ha_mcp_dev-7.3.0.dev400}/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
|