ha-mcp-dev 8.3.0.dev2445__tar.gz → 8.3.0.dev2450__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-8.3.0.dev2445/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.3.0.dev2450}/PKG-INFO +1 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/pyproject.toml +1 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +1 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +1 -1
- ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/AGENTS.md +40 -0
- ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/CLAUDE.md +40 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/README.md +1 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +14 -8
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +30 -0
- ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/backups.md +128 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +4 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +1 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/server.py +192 -2
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/en.json +2 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.js +126 -36
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/backup.py +18 -3
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_dashboards.py +15 -2
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/skill_loader.py +22 -4
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
- ha_mcp_dev-8.3.0.dev2445/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -57
- ha_mcp_dev-8.3.0.dev2445/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -57
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/MANIFEST.in +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/README.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/setup.cfg +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_rich_compat.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/requirements.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/version.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/oidc_compat.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/hacs_auto_refresh.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/log_filters.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/editing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/redaction.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/renamed_tools.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_locale_policy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/README.md +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/de.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/es.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/it.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/tlh.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_result.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_variables.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/bulk_selector.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_config_entries.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_identity.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/error_log_parsing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/hacs_registration.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_common.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_sources.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_sources_supervisor.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_logs.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_security_policy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/config_write_lock.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/entity_membership.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/enforcement.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/tests/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/tests/test_constants.py +0 -0
- {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/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 = "8.3.0.
|
|
7
|
+
version = "8.3.0.dev2450"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# Upper bound widened to <3.15 so the package installs on Home Assistant's
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "home-assistant-skills",
|
|
3
3
|
"description": "Agent skills for Home Assistant: best practices for automations, helpers, templates, device control, and dashboards.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.18.0",
|
|
5
5
|
"repository": "https://github.com/homeassistant-ai/skills",
|
|
6
6
|
"license": "MIT"
|
|
7
7
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Skill Format
|
|
6
|
+
|
|
7
|
+
Every skill is a `SKILL.md` file with YAML frontmatter:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
---
|
|
11
|
+
name: skill-name-here
|
|
12
|
+
description: >
|
|
13
|
+
When to activate this skill and what symptoms it addresses.
|
|
14
|
+
---
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Full authoring constraints: `CONTRIBUTING.md`. The one CI cannot catch:
|
|
18
|
+
- `metadata.version` must be `0` on new skills — do not edit manually; CI assigns the real version on merge
|
|
19
|
+
|
|
20
|
+
## Skill Authoring Principles
|
|
21
|
+
|
|
22
|
+
- **Context window conservation** — only include domain-specific knowledge that agents lack; omit general programming advice
|
|
23
|
+
- **Conciseness** — provide patterns and quick-reference tables, not tutorials
|
|
24
|
+
- **Consistent terminology** — one term per concept throughout a skill
|
|
25
|
+
- **Symptom-based triggering** — the `description` frontmatter should describe observable agent behaviors that signal the skill is needed
|
|
26
|
+
- **No tool names** — reference HA REST APIs and concepts, never specific MCP tool names (e.g. `ha_rename_entity`); tool names vary by agent setup
|
|
27
|
+
|
|
28
|
+
## Validation
|
|
29
|
+
|
|
30
|
+
To validate locally:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uvx skills-ref validate skills/<skill-name>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Reviewing Skill PRs
|
|
37
|
+
|
|
38
|
+
- Judge prose as agent-consumed context, not human docs — the Skill Authoring Principles above are the review bar (e.g. an operator→result lookup table beats narrative bullets, because agents land here holding one case to resolve)
|
|
39
|
+
- Skills make version-pinned claims about HA behavior — verify against source at the release tag: `gh api repos/home-assistant/core/contents/<path>?ref=2026.7.4 --jq .content | base64 -d`
|
|
40
|
+
- Community PRs come from forks: base-repo `?ref=<pr-branch>` 404s; get the fork with `gh pr view <pr> --json headRepository` and fetch files from there
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Skill Format
|
|
6
|
+
|
|
7
|
+
Every skill is a `SKILL.md` file with YAML frontmatter:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
---
|
|
11
|
+
name: skill-name-here
|
|
12
|
+
description: >
|
|
13
|
+
When to activate this skill and what symptoms it addresses.
|
|
14
|
+
---
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Full authoring constraints: `CONTRIBUTING.md`. The one CI cannot catch:
|
|
18
|
+
- `metadata.version` must be `0` on new skills — do not edit manually; CI assigns the real version on merge
|
|
19
|
+
|
|
20
|
+
## Skill Authoring Principles
|
|
21
|
+
|
|
22
|
+
- **Context window conservation** — only include domain-specific knowledge that agents lack; omit general programming advice
|
|
23
|
+
- **Conciseness** — provide patterns and quick-reference tables, not tutorials
|
|
24
|
+
- **Consistent terminology** — one term per concept throughout a skill
|
|
25
|
+
- **Symptom-based triggering** — the `description` frontmatter should describe observable agent behaviors that signal the skill is needed
|
|
26
|
+
- **No tool names** — reference HA REST APIs and concepts, never specific MCP tool names (e.g. `ha_rename_entity`); tool names vary by agent setup
|
|
27
|
+
|
|
28
|
+
## Validation
|
|
29
|
+
|
|
30
|
+
To validate locally:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uvx skills-ref validate skills/<skill-name>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Reviewing Skill PRs
|
|
37
|
+
|
|
38
|
+
- Judge prose as agent-consumed context, not human docs — the Skill Authoring Principles above are the review bar (e.g. an operator→result lookup table beats narrative bullets, because agents land here holding one case to resolve)
|
|
39
|
+
- Skills make version-pinned claims about HA behavior — verify against source at the release tag: `gh api repos/home-assistant/core/contents/<path>?ref=2026.7.4 --jq .content | base64 -d`
|
|
40
|
+
- Community PRs come from forks: base-repo `?ref=<pr-branch>` 404s; get the fork with `gh pr view <pr> --json headRepository` and fetch files from there
|
{ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
@@ -76,6 +76,7 @@ The `home-assistant-best-practices` skill includes:
|
|
|
76
76
|
| `references/examples.yaml` | Compound examples combining multiple best practices |
|
|
77
77
|
| `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact |
|
|
78
78
|
| `references/blueprint-guide.md` | Blueprint authoring: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, `!input` templating, versioning |
|
|
79
|
+
| `references/backups.md` | Full instance backups vs rolling one object back, when an operation needs a backup first, what an archive contains, encryption keys, restore verification, backup deletion |
|
|
79
80
|
|
|
80
81
|
## Contributing
|
|
81
82
|
|
|
@@ -5,14 +5,14 @@ description: >
|
|
|
5
5
|
|
|
6
6
|
TRIGGER THIS SKILL WHEN:
|
|
7
7
|
- Creating or editing automations, scripts, scenes, or dashboards
|
|
8
|
-
- Choosing
|
|
9
|
-
- Restructuring triggers, conditions, or
|
|
10
|
-
-
|
|
8
|
+
- Choosing template sensors vs built-in helpers
|
|
9
|
+
- Restructuring triggers, conditions, or modes
|
|
10
|
+
- Zigbee button/remote automations
|
|
11
11
|
- Renaming entities or migrating device_id to entity_id
|
|
12
|
-
- Configuring dashboard cards or selecting helpers
|
|
13
12
|
- Looking up card types or domain docs
|
|
14
|
-
-
|
|
15
|
-
- Authoring
|
|
13
|
+
- AppDaemon apps
|
|
14
|
+
- Authoring reusable Blueprints
|
|
15
|
+
- About to delete a backup, restore a backup, or upgrade Core or the OS
|
|
16
16
|
|
|
17
17
|
SYMPTOMS:
|
|
18
18
|
- Agent uses Jinja2 templates where native options exist
|
|
@@ -22,9 +22,10 @@ description: >
|
|
|
22
22
|
- Agent hard-codes values or uses raw sensor over helper
|
|
23
23
|
- Agent edits .storage, writes YAML, or generates YAML snippets
|
|
24
24
|
- Agent tells user to edit configuration.yaml for UI integrations
|
|
25
|
-
- Agent hardcodes entities
|
|
25
|
+
- Agent hardcodes Blueprint entities or uses free-text over a selector
|
|
26
|
+
- Agent changes existing state with no recovery path
|
|
26
27
|
metadata:
|
|
27
|
-
version:
|
|
28
|
+
version: 18
|
|
28
29
|
---
|
|
29
30
|
|
|
30
31
|
# Home Assistant Best Practices
|
|
@@ -122,6 +123,10 @@ See `references/device-control.md#zigbee-buttonremote-patterns`.
|
|
|
122
123
|
| Free-text input for an entity/device in a Blueprint | Typed `entity`/`target`/`device` selector | Text lets typos through and fails silently; selectors validate the choice | `references/blueprint-guide.md#inputs-and-selectors` |
|
|
123
124
|
| `!input` used directly inside a template | Bind it to a `variables:` entry, use the variable | `!input` is a YAML tag, not a template value — the template errors or ignores it | `references/blueprint-guide.md#referencing-inputs-input-and-templating` |
|
|
124
125
|
| Publishing a Blueprint without `source_url` | Set `source_url` to the file's canonical URL | Without it users can't re-import updates and sharing is awkward | `references/blueprint-guide.md#blueprint-metadata` |
|
|
126
|
+
| Full instance restore to undo one automation/script/scene/dashboard/helper edit | Roll that object back — write its previous definition back through the config API | A full restore reverts every unrelated change made since the backup and restarts HA | `references/backups.md#which-recovery-path` |
|
|
127
|
+
| Deleting a device/entity/integration, or upgrading Core, without a preceding backup | Take the full backup *before* the operation | Registry deletions can't be undone by writing the old value back, and a backup taken afterward captures the damage | `references/backups.md#when-a-full-backup-earns-its-cost` |
|
|
128
|
+
| Restoring a backup, deleting a backup, or upgrading Core or the OS without explicit user confirmation | Ask, name the concrete effect, and wait for an answer — every time, backup or not | A full restore discards everything since the archive for all restored parts and restarts HA; a Supervisor partial restore overwrites only the selected archive parts; deletion destroys a recovery point; a Core/OS upgrade is high-impact and its recovery path IS the pre-upgrade backup | `references/backups.md` |
|
|
129
|
+
| Calling a service "reversible" without naming its inverse | Treat an operation as reversible only when you can name and target its exact inverse | `vacuum.send_command`, `*.press` and `notify.*` have none; and an exact inverse still doesn't undo the consequence — re-locking a door doesn't un-expose the house | `references/backups.md#when-a-full-backup-earns-its-cost` |
|
|
125
130
|
|
|
126
131
|
---
|
|
127
132
|
|
|
@@ -144,3 +149,4 @@ Read these when you need detailed information:
|
|
|
144
149
|
| `references/examples.yaml` | Need compound examples combining multiple best practices | — |
|
|
145
150
|
| `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact | — |
|
|
146
151
|
| `references/blueprint-guide.md` | Authoring reusable blueprints: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, input sections, `!input` templating, versioning | `#when-to-author-a-blueprint`, `#blueprint-metadata`, `#inputs-and-selectors`, `#target-selector-vs-entity-selector`, `#defaults`, `#input-sections`, `#referencing-inputs-input-and-templating`, `#versioning-and-updates`, `#common-pitfalls` |
|
|
152
|
+
| `references/backups.md` | Deciding whether an operation needs a backup first; choosing between a full restore, a partial restore, and rolling one object back; what an archive actually contains; encryption keys and the emergency kit; restore verification; what HA does and does not protect when deleting a backup | `#which-recovery-path`, `#when-a-full-backup-earns-its-cost`, `#what-a-full-backup-contains`, `#restoring-consequences-and-verification`, `#deleting-backups`, `#common-pitfalls` |
|
|
@@ -927,6 +927,36 @@ variables:
|
|
|
927
927
|
|
|
928
928
|
`trigger_variables:` is a separate top-level key evaluated **before** triggers fire — it supports **limited templates only** (no `states()`/`state_attr()`), mainly for passing a blueprint `!input` into trigger options. Don't put state-based templates there.
|
|
929
929
|
|
|
930
|
+
### Keys render in order, one at a time
|
|
931
|
+
|
|
932
|
+
A `variables:` block renders one key at a time, each rendered result feeding the context for the next. A key that reads a name declared **further down the same block** reads it while it is still undefined.
|
|
933
|
+
|
|
934
|
+
```yaml
|
|
935
|
+
# AVOID — `total` is still undefined when `msg` renders
|
|
936
|
+
variables:
|
|
937
|
+
msg: "Total: {{ total }}"
|
|
938
|
+
total: "{{ states('sensor.a') | float(0) + states('sensor.b') | float(0) }}"
|
|
939
|
+
|
|
940
|
+
# CORRECT — declare before use
|
|
941
|
+
variables:
|
|
942
|
+
total: "{{ states('sensor.a') | float(0) + states('sensor.b') | float(0) }}"
|
|
943
|
+
msg: "Total: {{ total }}"
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
What the forward read costs depends on how the template uses the name (behavior as of 2026.7):
|
|
947
|
+
|
|
948
|
+
| Use of the undefined name | Result |
|
|
949
|
+
|---|---|
|
|
950
|
+
| `x.attr`, `x['k']`, arithmetic, `<`/`>`/`<=`/`>=`, `\| int`, `\| float` | Raises `UndefinedError`, logs ERROR — **aborts the run** |
|
|
951
|
+
| `{{ x }}`, `{% if x %}`, `{% for i in x %}`, `'p' ~ x` | Empty and falsy, logs WARNING — runs on into the else branch |
|
|
952
|
+
| `x \| length`, `x == y`, `x != y` | `0` / `False` / `True` — **no log and no trace entry** |
|
|
953
|
+
|
|
954
|
+
For the aborting row, [`continue_on_error:`](#continue-on-error) is no escape — it is an action option, a top-level block is not an action, and on a mid-sequence step it cannot suppress a render error. A default like `| int(0)` does not save it either: the default covers an unconvertible value, not an undefined name. In the silent row, watch `!=`: `{% if later != 'x' %}` on a name that is not defined yet takes the **then** branch, leaving nothing behind to explain why.
|
|
955
|
+
|
|
956
|
+
Not a problem when the name also exists in an enclosing scope — the reference then resolves outward to that value. The two placements diverge on that collision, though: a top-level `variables:` block **skips** a key already present in the incoming scope, so the incoming value stands and the block's own definition never takes effect for that run, while a mid-sequence `- variables:` step renders and **overwrites** it. Staging a computation across consecutive `variables:` steps is therefore mid-sequence only.
|
|
957
|
+
|
|
958
|
+
When an automation declares both `trigger_variables:` and `variables:`, the two render as one sequential mapping with `trigger_variables:` first, so a `variables:` key may read a `trigger_variables:` key but not the reverse. That holds for names declared in only one of the two: a name in both merges into a single entry that keeps the `trigger_variables:` position but takes the `variables:` template, so it renders early — before any `trigger_variables:` key declared after it. The separate attach-time render noted above happens before any `variables:` key exists.
|
|
959
|
+
|
|
930
960
|
---
|
|
931
961
|
|
|
932
962
|
## Capturing Action Responses
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Backups and Recovery
|
|
2
|
+
|
|
3
|
+
**Three different things get called a "backup" in Home Assistant work.** This file covers the first two, and only the first is a Home Assistant feature:
|
|
4
|
+
|
|
5
|
+
1. A **full instance backup** — the archive Home Assistant creates at *Settings → System → Backups*, which is also the surface for restoring and deleting one.
|
|
6
|
+
2. **Object rollback** — not an HA feature but a workflow: fetch an object's current definition *before* editing it, then write that definition back through the same config API to undo. One object, no restart.
|
|
7
|
+
3. The **pre-edit file copy** a managed YAML write takes — a different mechanism entirely, covered in `references/yaml-only-integrations.md`.
|
|
8
|
+
|
|
9
|
+
**Three operations need explicit user confirmation before you act — every time, including when a backup already exists.** They differ in kind, so say which one applies:
|
|
10
|
+
|
|
11
|
+
- **Restoring a backup** — *irreversible*: it discards every change made since that archive and restarts HA. The only way back is another backup taken before the restore.
|
|
12
|
+
- **Deleting a backup** — *irreversible*: the archive is gone, and with it any recovery point it was the last copy of.
|
|
13
|
+
- **Upgrading Core or the OS** — *high-impact rather than strictly irreversible*: it can break integrations and custom components, and the recovery path is restoring the pre-upgrade backup, which is why one has to exist first.
|
|
14
|
+
|
|
15
|
+
A backup lowers recovery risk; it does not authorize the action.
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
1. [Which Recovery Path](#which-recovery-path)
|
|
19
|
+
2. [When a Full Backup Earns Its Cost](#when-a-full-backup-earns-its-cost)
|
|
20
|
+
3. [What a Full Backup Contains](#what-a-full-backup-contains)
|
|
21
|
+
4. [Restoring: Consequences and Verification](#restoring-consequences-and-verification)
|
|
22
|
+
5. [Deleting Backups](#deleting-backups)
|
|
23
|
+
6. [Common Pitfalls](#common-pitfalls)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Which Recovery Path
|
|
28
|
+
|
|
29
|
+
| Situation | What to do |
|
|
30
|
+
|-----------|------------|
|
|
31
|
+
| One automation / script / scene / dashboard / helper edit went wrong | **Roll the object back** — write its previous definition back through the config API. No restart, nothing else touched. Fetching before writing is what makes this possible; see `references/safe-refactoring.md#universal-workflow`. |
|
|
32
|
+
| Several objects were edited in one session | **Roll each object back**, one at a time. Still no restart. |
|
|
33
|
+
| A device, entity, or area was deleted from the registry | **Restore a full backup.** Registry state — IDs, area/label assignments, and the references other config held to them — cannot be recovered by writing a definition back. |
|
|
34
|
+
| An integration or config entry was removed | **Restore a full backup.** Its entities went with it. |
|
|
35
|
+
| An App update or removal broke something | **Restore a full backup partially** — that App and its data only. Supervisor installs (OS/Supervised) only; elsewhere it is a full restore. |
|
|
36
|
+
| A Core or Operating System upgrade broke the instance | **Restore a full backup.** |
|
|
37
|
+
| The instance no longer boots | **Restore a full backup** through the Supervisor / onboarding restore flow. |
|
|
38
|
+
|
|
39
|
+
Prefer the narrowest path that can fix the fault. Object rollback is scoped to one object, needs no restart, and cannot lose unrelated work.
|
|
40
|
+
|
|
41
|
+
**Partial restore is a narrower restore, not a rollback.** HA's partial restore (`hassio.restore_partial`) picks which parts of an existing backup to put back — Home Assistant settings, specific Apps, and folders. Including Home Assistant settings restarts HA. That is a different mechanism from writing one object's definition back through the config API, which restarts nothing.
|
|
42
|
+
|
|
43
|
+
**Partial restore needs a Supervisor**, so it exists on Home Assistant OS and Supervised only. On Container and Core installs the `hassio` integration is not loaded, there is no partial restore, and the recovery options are object rollback or a full restore through whatever backup mechanism that install uses. Check which install you are on before offering a partial restore as a step.
|
|
44
|
+
|
|
45
|
+
## When a Full Backup Earns Its Cost
|
|
46
|
+
|
|
47
|
+
The test is reversibility, not how risky something feels: **back up before an operation you cannot undo by writing the old value back.**
|
|
48
|
+
|
|
49
|
+
**Irreversible — take the backup first, and confirm the operation with the user:**
|
|
50
|
+
- Deleting a device, entity, or area from the registry.
|
|
51
|
+
- Removing an integration or config entry.
|
|
52
|
+
- Removing an App, or a major App version jump.
|
|
53
|
+
- Core / Operating System upgrades.
|
|
54
|
+
- Restoring a *different* backup — a restore is itself destructive of current state.
|
|
55
|
+
- Bulk operations across many objects whose before-state was not captured.
|
|
56
|
+
- Editing a Config-Entry integration whose fields have no post-setup API write path — see `references/safe-refactoring.md#config-entry-data--blind-spots-for-entity-registry-renames`.
|
|
57
|
+
|
|
58
|
+
**Reversible — a backup is usually redundant:**
|
|
59
|
+
- Editing an automation / script / scene / dashboard / helper whose current definition was fetched first. Writing that definition back *is* the undo.
|
|
60
|
+
- Renaming a friendly name, changing an icon, moving an entity between areas.
|
|
61
|
+
- A service call whose inverse you can name and target identically — `light.turn_on` ↔ `light.turn_off` on the same entity.
|
|
62
|
+
|
|
63
|
+
**Neither list — treat as irreversible until you have checked.** The exact-inverse test is the rule; these are the cases where passing it still is not enough:
|
|
64
|
+
|
|
65
|
+
- **No inverse exists.** `vacuum.send_command`, any `*.press`, `notify.*` — nothing to call to undo them.
|
|
66
|
+
- **The inverse exists but does not undo the consequence.** `lock.unlock` ↔ `lock.lock` and `cover.open_cover` ↔ `cover.close_cover` are exact inverses, yet the door was unlocked and the cover was open in between. Re-locking restores the state, not the exposure. Confirm these with the user first even though they are mechanically reversible.
|
|
67
|
+
- **The effect has already left Home Assistant.** A sent notification or a fired webhook cannot be recalled, whatever you call next.
|
|
68
|
+
|
|
69
|
+
"It's just a service call" is not a reversibility argument — the inverse depends on the service and the target, and a reversible state change is not always a reversible event.
|
|
70
|
+
|
|
71
|
+
Two things this test depends on:
|
|
72
|
+
|
|
73
|
+
- **Timing is the whole point.** A backup taken *after* the destructive step captures the damage. It must precede the operation.
|
|
74
|
+
- **An existing schedule does not substitute.** On an instance with nightly automatic backups, the newest one can be almost a day stale — fine for reversible edits, not for an irreversible operation about to run now.
|
|
75
|
+
|
|
76
|
+
## What a Full Backup Contains
|
|
77
|
+
|
|
78
|
+
Backup contents are **selected per backup**, so what a given archive holds is a property of that archive, not of Home Assistant:
|
|
79
|
+
|
|
80
|
+
- **Home Assistant settings** — the config directory: `.storage` registries, YAML configuration, and the stored automations, scripts, scenes, dashboards, and helpers.
|
|
81
|
+
- **The recorder database — usually included, but confirm rather than assume.** Home Assistant Core's own backup config defaults `include_database` to true (2026.8.1), so history and long-term statistics normally *are* in the archive and *do* come back. Three things change that answer: the option can be switched off for an individual backup; on Supervised/OS the Supervisor API takes `homeassistant_exclude_database` per backup and the `backups_exclude_database` setting can make exclusion the *default* for every future one; and a recorder pointed at an external database lives outside the archive entirely. Check the archive and the install before telling a user their history will or won't return.
|
|
82
|
+
- **Apps**, individually selectable, with their data directories.
|
|
83
|
+
- **Folders**, individually selectable: `share`, `addons/local`, `ssl`, `media`.
|
|
84
|
+
|
|
85
|
+
Nothing else on the host is included, and a restore overwrites only the parts a given backup actually contains.
|
|
86
|
+
|
|
87
|
+
**Encryption and secrets.** Backups are password-protected by default, and restoring one requires its encryption key — the key issued in the backup emergency kit when HA sets up encrypted backups. **An archive without its key is not a recovery point**, so verify the user has the emergency kit before treating a backup as a fallback. A backup downloaded through the UI is decrypted on the way out, and that copy carries `.storage` in cleartext — credentials, long-lived tokens, API keys. Protect the archive and the emergency kit as separate secrets, and never move either anywhere the user has not chosen.
|
|
88
|
+
|
|
89
|
+
## Restoring: Consequences and Verification
|
|
90
|
+
|
|
91
|
+
- **Confirm before restoring.** Naming which changes will be lost is part of the ask.
|
|
92
|
+
- A **full restore** reverts every included part to its state at backup time and restarts HA. Unrelated work done in that window is gone.
|
|
93
|
+
- A **partial restore** narrows the blast radius; prefer it whenever the fault is localised. Including Home Assistant settings still restarts HA.
|
|
94
|
+
- An **object rollback** through the config API restarts nothing and touches one object.
|
|
95
|
+
|
|
96
|
+
After any restore, verify rather than assume:
|
|
97
|
+
- Entities are available, not `unavailable` — a restored config entry whose credentials expired comes back broken.
|
|
98
|
+
- The error log is clean of startup failures.
|
|
99
|
+
- Cloud-backed integrations may need re-authentication; a restore replays a token that may have been rotated since.
|
|
100
|
+
- Objects that were disabled when the backup was taken come back disabled.
|
|
101
|
+
|
|
102
|
+
## Deleting Backups
|
|
103
|
+
|
|
104
|
+
Be precise about what Home Assistant protects here, because it is less than it looks:
|
|
105
|
+
|
|
106
|
+
- The **retention cleanup** that enforces a backup schedule will not delete the last remaining backup for a backup location.
|
|
107
|
+
- A **direct delete of one specific backup** — the operation performed when a user asks for a particular backup to be removed — has **no such guard**, and there is no minimum-age protection anywhere. Delete the only recovery point and Home Assistant will carry it out.
|
|
108
|
+
|
|
109
|
+
So on this operation the caution has to come from whoever is driving, not from the platform:
|
|
110
|
+
|
|
111
|
+
- **Confirm the specific backup with the user before deleting it** — name which one and how old it is.
|
|
112
|
+
- **Leave at least one recent, usable recovery point.** Usable includes having its encryption key available.
|
|
113
|
+
- **Delete oldest first** when clearing several.
|
|
114
|
+
|
|
115
|
+
Storage pressure is not the only legitimate reason to delete. A credential compromise can make the archive itself the liability; a privacy or retention requirement can mandate removal; and a user can simply ask. None of those removes the confirmation step or the keep-one-recovery-point guidance.
|
|
116
|
+
|
|
117
|
+
## Common Pitfalls
|
|
118
|
+
|
|
119
|
+
- **Backing up after the destructive step.** The archive now contains the damage.
|
|
120
|
+
- **Using a full restore to undo one config edit.** It reverts every unrelated change since, and restarts HA.
|
|
121
|
+
- **Assuming a direct backup delete is guarded.** It isn't — only the scheduled retention cleanup keeps a last backup.
|
|
122
|
+
- **Treating an archive without its encryption key as a recovery point.** It cannot be restored.
|
|
123
|
+
- **Treating a UI-downloaded backup as an ordinary file.** It is decrypted, and it holds cleartext credentials and tokens.
|
|
124
|
+
- **Claiming history will or won't come back without checking.** The recorder database is included by default, but per-backup selection and external databases both change the answer.
|
|
125
|
+
- **Calling a service "reversible" without naming its inverse** — or treating a mechanical inverse as undoing the consequence, when the door was still unlocked in between.
|
|
126
|
+
- **Treating a backup as a substitute for fetching the current definition before an edit.** Fetching is cheaper, object-scoped, and needs no restart to undo.
|
|
127
|
+
- **Keeping backups only on the instance they back up.** One disk or host failure takes the instance and its recovery points together.
|
|
128
|
+
- **Acting on an irreversible operation because a backup exists.** The backup is the safety net, not the authorization — the user's confirmation is.
|
|
@@ -22,7 +22,7 @@ This document covers Home Assistant's built-in helpers and integrations that sho
|
|
|
22
22
|
Helpers reach Home Assistant through two different creation mechanisms — which one a helper uses determines whether you submit flat fields in a single step or step through a config flow:
|
|
23
23
|
|
|
24
24
|
- **Storage-collection helpers** — created via a per-domain WebSocket collection command (`<domain>/create`, e.g. `input_boolean/create`, `counter/create`) with flat, structured fields: `input_boolean`, `input_number`, `input_select`, `input_text`, `input_datetime`, `input_button`, `counter`, `timer`, `schedule`, `zone`, `person`, `tag`. (`schedule` additionally offers an optional YAML mode.)
|
|
25
|
-
- **Config-entry-flow helpers** — created through the generic config-entry flow (`config_entries/flow`, handler = the helper domain), often a multi-step flow that begins with a sub-type menu (see [Menu-Based Helpers](#menu-based-helpers)): `template`, `group`, `utility_meter`, `derivative`, `min_max`, `threshold`, `integration`, `statistics`, `trend`, `random`, `filter`, `tod`, `generic_thermostat`, `generic_hygrostat`, `switch_as_x`, `bayesian`, `mold_indicator`.
|
|
25
|
+
- **Config-entry-flow helpers** — created through the generic config-entry flow (`config_entries/flow`, handler = the helper domain), often a multi-step flow that begins with a sub-type menu (see [Menu-Based Helpers](#menu-based-helpers)): `template`, `group`, `utility_meter`, `derivative`, `min_max`, `threshold`, `integration`, `statistics`, `trend`, `random`, `filter`, `tod`, `generic_thermostat`, `generic_hygrostat`, `switch_as_x`, `bayesian`, `mold_indicator`, `history_stats`.
|
|
26
26
|
|
|
27
27
|
## Menu-Based Helpers
|
|
28
28
|
|
|
@@ -357,6 +357,9 @@ sensor:
|
|
|
357
357
|
**Key behaviors:**
|
|
358
358
|
- Limited by recorder's `purge_keep_days`
|
|
359
359
|
- Updates when source changes or once per minute
|
|
360
|
+
- Creatable via the config-entry flow (multi-step: entity and type, then
|
|
361
|
+
tracked states, then the period — exactly two of `start`/`end`/`duration`);
|
|
362
|
+
YAML remains supported but is not required
|
|
360
363
|
|
|
361
364
|
**Common uses:**
|
|
362
365
|
- How long lights were on today
|
|
@@ -205,7 +205,7 @@ Iterate the returned entries and check `data` and `options` fields for the old e
|
|
|
205
205
|
|
|
206
206
|
For integrations that store entity_ids in `options` (Generic Thermostat, Generic Hygrostat, Threshold Helper, Min/Max Helper): use the Options Flow. See the Config-Entry-Groups section above for the full Options Flow pattern.
|
|
207
207
|
|
|
208
|
-
For integrations that store entity_ids in `data` (Better Thermostat): `data` fields written during the initial Config Flow setup have no standard API for post-setup mutation — the Options Flow updates `options` only. No API-based fix path exists. Document this limitation to the user before proceeding with a rename.
|
|
208
|
+
For integrations that store entity_ids in `data` (Better Thermostat): `data` fields written during the initial Config Flow setup have no standard API for post-setup mutation — the Options Flow updates `options` only. No API-based fix path exists. Document this limitation to the user before proceeding with a rename. This is the one operation in this guide that a fetch-before-write rollback cannot undo, so treat the rename as irreversible and take a full backup first — see `references/backups.md#when-a-full-backup-earns-its-cost`.
|
|
209
209
|
|
|
210
210
|
---
|
|
211
211
|
|
|
@@ -828,10 +828,152 @@ class HomeAssistantSmartMCPServer:
|
|
|
828
828
|
'mean "search failed", not "no results" — see `partial_reason` '
|
|
829
829
|
"(also mirrored into `warnings`). Do not treat a partial response as "
|
|
830
830
|
"complete.\n\n"
|
|
831
|
-
"For
|
|
831
|
+
"For what to do with what you find — native-first patterns, "
|
|
832
|
+
"entity_id over device_id, impact analysis before renaming — see "
|
|
833
|
+
"ha_get_skill_guide."
|
|
834
|
+
),
|
|
835
|
+
# ha_manage_backup: the full description is 4571 dedented chars at
|
|
836
|
+
# BACKUP_HINT=normal (4672 strong / 4598 weak) and the lite value is
|
|
837
|
+
# a little over a quarter of that. No exact pair is quoted here on
|
|
838
|
+
# purpose — three successive edits left a stale figure in this
|
|
839
|
+
# comment, so the ratio is pinned by
|
|
840
|
+
# test_backup_lite_description_stays_substantially_smaller instead,
|
|
841
|
+
# which measures both sides at test time.
|
|
842
|
+
#
|
|
843
|
+
# It was the largest full description in the GATEWAY catalog this was
|
|
844
|
+
# measured against, not in the repo: ha_eval_template (5834) and
|
|
845
|
+
# ha_get_system_health (5811) are both larger and both unmapped.
|
|
846
|
+
# The reduction is smaller than pure compression would give because
|
|
847
|
+
# the safety content below is kept inline. The routing matrix STAYS —
|
|
848
|
+
# the `action` parameter's own Field description says "Valid (scope,
|
|
849
|
+
# action) combinations are listed in the tool description", so
|
|
850
|
+
# trimming it away would leave that pointer aimed at nothing.
|
|
851
|
+
#
|
|
852
|
+
# The deferral target now exists: homeassistant-ai/skills#76 landed
|
|
853
|
+
# references/backups.md, and the submodule pin in this commit
|
|
854
|
+
# includes it, so test_every_lite_destination_resolves enforces it
|
|
855
|
+
# rather than the entry sitting on "self-contained". What defers is
|
|
856
|
+
# the recovery-layer judgment — which of HA's two paths fits which
|
|
857
|
+
# failure, what an archive actually contains, encryption keys, and
|
|
858
|
+
# what HA does and does not protect on delete. The eleven worked
|
|
859
|
+
# examples stay dropped: they were call syntax, which the input
|
|
860
|
+
# schema already carries.
|
|
861
|
+
#
|
|
862
|
+
# destructiveHint is set on this tool, so the lite text keeps every
|
|
863
|
+
# irreversibility marker inline rather than deferring it: the
|
|
864
|
+
# restart, the confirm, the human-only enable_snapshot_delete, and
|
|
865
|
+
# each individual delete guard. It also keeps the two diagnostics
|
|
866
|
+
# that have no fallback anywhere else — the enable_auto_backup
|
|
867
|
+
# empty-list ambiguity, and the {backup_hint_text} timing sentence
|
|
868
|
+
# (resolved per-instance; see _lite_docstring_tokens).
|
|
869
|
+
"ha_manage_backup": (
|
|
870
|
+
"Manage Home Assistant backups: full HA snapshots "
|
|
871
|
+
"(`scope='snapshot'`) and per-entity auto-backups of agent edits "
|
|
872
|
+
"(`scope='edits'`). Pick the scope first — the wrong one routes "
|
|
873
|
+
"through the wrong code path.\n\n"
|
|
874
|
+
"`scope='snapshot'` actions: `create` (slow on a large "
|
|
875
|
+
"instance — progress heartbeats are sent while waiting, so a "
|
|
876
|
+
"long wait is not a hang; retrying starts a SECOND backup), "
|
|
877
|
+
"`list`, `restore` (**restarts HA**), `delete` (needs "
|
|
878
|
+
"`confirm=True`; disabled until a human sets "
|
|
879
|
+
"`enable_snapshot_delete` — an agent cannot enable it. Even then "
|
|
880
|
+
"a delete is refused for scheduled/automatic backups, for "
|
|
881
|
+
"anything younger than `snapshot_delete_min_age_days` (default "
|
|
882
|
+
"7; 0 disables the floor), and for the single newest snapshot "
|
|
883
|
+
"remaining).\n\n"
|
|
884
|
+
"`scope='edits'` actions: `create` (needs `domain` + "
|
|
885
|
+
"`entity_id`), `list`, `view`, `diff`, `restore` (takes a fresh "
|
|
886
|
+
"safety snapshot first; no HA restart), `delete`. Automatic "
|
|
887
|
+
"capture on write is gated by `enable_auto_backup`, so an empty "
|
|
888
|
+
'`list` means "nothing saved" OR "the toggle is off" — check it '
|
|
889
|
+
"before concluding there is nothing to restore. Either way "
|
|
890
|
+
"`(edits, create)` still works: it bypasses the toggle because "
|
|
891
|
+
"the request is explicit.\n\n"
|
|
892
|
+
"Use `edits` to undo a recent agent edit to an "
|
|
893
|
+
"automation/script/scene/dashboard/helper; use `snapshot` for "
|
|
894
|
+
"system-wide recovery and before irreversible operations. "
|
|
895
|
+
"{backup_hint_text}\n\n"
|
|
896
|
+
"For which recovery path fits which failure, what an archive "
|
|
897
|
+
"actually contains, and the encryption key a restore needs, see "
|
|
898
|
+
"ha_get_skill_guide (`references/backups.md`)."
|
|
899
|
+
),
|
|
900
|
+
# ha_report_issue: the dedented docstring is what gets advertised,
|
|
901
|
+
# not the raw indented one (2351 chars), and the lite value is about
|
|
902
|
+
# a third of it. Same reason as above for not quoting a pair.
|
|
903
|
+
#
|
|
904
|
+
# Unlike every other entry here, the deferral target is the tool's
|
|
905
|
+
# OWN RESPONSE, not the skill guide: `instructions` (see
|
|
906
|
+
# tools_bug_report.py) independently re-derives the duplicate check,
|
|
907
|
+
# the template choice, the missing-tool pre-check, and the mandatory
|
|
908
|
+
# anonymisation step. Issue reporting is ha-mcp product meta — it
|
|
909
|
+
# cannot go in the skill pack, whose CONTRIBUTING forbids coupling
|
|
910
|
+
# skill content to specific MCP tool names. The lite text says so
|
|
911
|
+
# outright so a compliant agent doesn't spend a call finding out.
|
|
912
|
+
"ha_report_issue": (
|
|
913
|
+
"Get diagnostic information plus a ready-to-file report "
|
|
914
|
+
"template. Covers two kinds of report: a runtime bug (ha-mcp "
|
|
915
|
+
"errored or behaved unexpectedly) and agent-behaviour feedback "
|
|
916
|
+
"(the AI used the wrong tool or worked inefficiently). Pick "
|
|
917
|
+
"based on whether the fault was in ha-mcp or in the agent's own "
|
|
918
|
+
"choices.\n\n"
|
|
919
|
+
"The response carries the full workflow in its `instructions` "
|
|
920
|
+
"field — duplicate check, template selection, the mandatory "
|
|
921
|
+
"anonymisation step, and the submit URLs — plus "
|
|
922
|
+
'`missing_tool_hint` for the "a tool I expected is missing" '
|
|
923
|
+
"case, which is usually a stale client tool list rather than a "
|
|
924
|
+
"bug. Read `instructions` before presenting anything to the "
|
|
925
|
+
"user; ha_get_skill_guide does not cover issue reporting."
|
|
832
926
|
),
|
|
833
927
|
}
|
|
834
928
|
|
|
929
|
+
# Where each _LITE_DOCSTRINGS entry's "see ..." pointer actually lands.
|
|
930
|
+
#
|
|
931
|
+
# The pointer is the whole bargain of lite mode: the trimmed text is only
|
|
932
|
+
# acceptable because the detail is reachable. Enforcing that every entry
|
|
933
|
+
# merely CONTAINS the string "ha_get_skill_guide" (the original
|
|
934
|
+
# invariant) checks the pointer and never the destination — which is how
|
|
935
|
+
# an entry deferring to guide content that does not exist could pass
|
|
936
|
+
# tests (#2153 review). This map names the destination so
|
|
937
|
+
# tests/src/unit/test_lite_docstrings.py can resolve it against the
|
|
938
|
+
# vendored skill pack.
|
|
939
|
+
#
|
|
940
|
+
# Three legal value forms, each with a matching check in the tests:
|
|
941
|
+
#
|
|
942
|
+
# "references/<file>.md" / "SKILL.md"
|
|
943
|
+
# A path inside the home-assistant-best-practices skill. Must
|
|
944
|
+
# resolve against the VENDORED pack, and the lite text must carry
|
|
945
|
+
# a ha_get_skill_guide pointer to reach it.
|
|
946
|
+
# "tool-response:<field>"
|
|
947
|
+
# The guidance ships in the tool's own response instead. The field
|
|
948
|
+
# must actually be returned, and the lite text must name it.
|
|
949
|
+
# "self-contained"
|
|
950
|
+
# The entry defers nothing. The lite text must carry NO
|
|
951
|
+
# ha_get_skill_guide pointer and name no reference file, so an
|
|
952
|
+
# entry cannot quietly re-acquire a pointer to content that isn't
|
|
953
|
+
# vendored — which is what "self-contained" exists to prevent.
|
|
954
|
+
_LITE_DOCSTRING_DESTINATIONS: ClassVar[dict[str, str]] = {
|
|
955
|
+
"ha_config_get_automation": "references/automation-patterns.md",
|
|
956
|
+
"ha_config_set_automation": "references/automation-patterns.md",
|
|
957
|
+
"ha_config_get_script": "references/automation-patterns.md",
|
|
958
|
+
"ha_config_set_script": "references/automation-patterns.md",
|
|
959
|
+
"ha_config_get_scene": "references/scenes.md",
|
|
960
|
+
"ha_config_set_scene": "references/scenes.md",
|
|
961
|
+
"ha_config_list_helpers": "references/helper-selection.md",
|
|
962
|
+
"ha_config_set_helper": "references/helper-selection.md",
|
|
963
|
+
"ha_config_get_dashboard": "references/dashboard-cards.md",
|
|
964
|
+
"ha_config_set_dashboard": "references/dashboard-guide.md",
|
|
965
|
+
"ha_call_service": "references/domain-docs.md",
|
|
966
|
+
"ha_config_set_yaml": "references/yaml-only-integrations.md",
|
|
967
|
+
# The skill pack has no search reference, so this lands on the
|
|
968
|
+
# decision workflow. The lite text was reworded to promise what
|
|
969
|
+
# SKILL.md actually holds (what to do with the results) instead of
|
|
970
|
+
# "parameters, schema, and examples", which it never had — the
|
|
971
|
+
# parameters ship in the input schema regardless.
|
|
972
|
+
"ha_search": "SKILL.md",
|
|
973
|
+
"ha_manage_backup": "references/backups.md",
|
|
974
|
+
"ha_report_issue": "tool-response:instructions",
|
|
975
|
+
}
|
|
976
|
+
|
|
835
977
|
# Description overrides that REPLACE the original description for BM25.
|
|
836
978
|
# Used to narrow overly broad tools so they stop matching generic queries
|
|
837
979
|
# against ha-mcp's internal BM25 search tool. Only applied when
|
|
@@ -840,6 +982,50 @@ class HomeAssistantSmartMCPServer:
|
|
|
840
982
|
# unnecessarily trim context for other clients.
|
|
841
983
|
_SEARCH_DESCRIPTION_OVERRIDES: ClassVar[dict[str, str]] = {}
|
|
842
984
|
|
|
985
|
+
@staticmethod
|
|
986
|
+
def _lite_docstring_tokens() -> dict[str, str]:
|
|
987
|
+
"""Operator-tuned values interpolated into the lite descriptions.
|
|
988
|
+
|
|
989
|
+
``_LITE_DOCSTRINGS`` is a static ``ClassVar``, but not every full
|
|
990
|
+
description it replaces is static: ``ha_manage_backup``'s is an
|
|
991
|
+
f-string built in ``register_backup_tools()`` that interpolates
|
|
992
|
+
``_get_backup_hint_text()`` from ``BACKUP_HINT``
|
|
993
|
+
(strong/normal/weak). Hard-coding one wording into the lite text
|
|
994
|
+
would make that setting a silent no-op whenever lite mode is on —
|
|
995
|
+
the add-on UI would confirm a Backup-hint change that changed
|
|
996
|
+
nothing. So the lite values carry ``{token}`` placeholders and are
|
|
997
|
+
resolved here, reading the same env var at the same point in
|
|
998
|
+
startup the full description does.
|
|
999
|
+
"""
|
|
1000
|
+
# Local import: same lazy-seam pattern as backup_manager's
|
|
1001
|
+
# _normalize_config_for_roundtrip import. It does NOT keep the tools
|
|
1002
|
+
# package off the module-load path — server.py already imports
|
|
1003
|
+
# .tools.helpers at module scope — what it avoids is pulling in
|
|
1004
|
+
# tools.backup, which is the actual reason to keep it local.
|
|
1005
|
+
from .tools.backup import _get_backup_hint_text
|
|
1006
|
+
|
|
1007
|
+
return {"backup_hint_text": _get_backup_hint_text()}
|
|
1008
|
+
|
|
1009
|
+
@classmethod
|
|
1010
|
+
def _resolve_lite_docstrings(cls) -> dict[str, str]:
|
|
1011
|
+
"""Return ``_LITE_DOCSTRINGS`` with its placeholders filled in.
|
|
1012
|
+
|
|
1013
|
+
Uses ``str.replace`` rather than ``str.format`` so the descriptions
|
|
1014
|
+
stay free to contain literal braces (Jinja examples, JSON) without
|
|
1015
|
+
a stray ``{`` turning into a ``KeyError`` at startup. An unknown
|
|
1016
|
+
placeholder is left verbatim rather than raising —
|
|
1017
|
+
``test_every_placeholder_in_the_map_is_resolvable`` is what catches a
|
|
1018
|
+
typo'd token, at test time instead of in a user's tool catalog.
|
|
1019
|
+
"""
|
|
1020
|
+
tokens = cls._lite_docstring_tokens()
|
|
1021
|
+
resolved: dict[str, str] = {}
|
|
1022
|
+
for name, lite in cls._LITE_DOCSTRINGS.items():
|
|
1023
|
+
filled = lite
|
|
1024
|
+
for token, value in tokens.items():
|
|
1025
|
+
filled = filled.replace("{" + token + "}", value)
|
|
1026
|
+
resolved[name] = filled
|
|
1027
|
+
return resolved
|
|
1028
|
+
|
|
843
1029
|
def _apply_lite_docstrings(self) -> None:
|
|
844
1030
|
"""Swap heavy tool descriptions for shorter variants if enabled.
|
|
845
1031
|
|
|
@@ -850,6 +1036,10 @@ class HomeAssistantSmartMCPServer:
|
|
|
850
1036
|
``ha_get_skill_guide``. Tools not in the
|
|
851
1037
|
mapping pass through unchanged.
|
|
852
1038
|
|
|
1039
|
+
Descriptions are resolved through ``_resolve_lite_docstrings`` so
|
|
1040
|
+
operator-tuned text (``BACKUP_HINT``) survives the swap instead of
|
|
1041
|
+
being pinned to whatever wording the static map happened to carry.
|
|
1042
|
+
|
|
853
1043
|
Emits a startup WARNING when enabled so non-addon users (Docker,
|
|
854
1044
|
uvx, pip) see the trade-off in their logs — the addon UI surfaces
|
|
855
1045
|
the same warning via the toggle description. A second WARNING is
|
|
@@ -884,7 +1074,7 @@ class HomeAssistantSmartMCPServer:
|
|
|
884
1074
|
|
|
885
1075
|
try:
|
|
886
1076
|
self.mcp.add_transform(
|
|
887
|
-
LiteDocstringsTransform(replacements=self.
|
|
1077
|
+
LiteDocstringsTransform(replacements=self._resolve_lite_docstrings())
|
|
888
1078
|
)
|
|
889
1079
|
except Exception:
|
|
890
1080
|
logger.exception("Failed to apply LiteDocstringsTransform")
|