ha-mcp-dev 8.3.0.dev2438__tar.gz → 8.3.0.dev2440__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.dev2438/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.3.0.dev2440}/PKG-INFO +1 -1
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/pyproject.toml +1 -1
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_i18n.py +247 -37
- ha_mcp_dev-8.3.0.dev2440/src/ha_mcp/settings_ui/_locale_policy.py +13 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/README.md +9 -0
- ha_mcp_dev-8.3.0.dev2440/src/ha_mcp/settings_ui/locales/tlh.json +832 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp_dev.egg-info/SOURCES.txt +2 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/MANIFEST.in +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/README.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/setup.cfg +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_rich_compat.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/requirements.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_vendor/websockets/version.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/auth/oidc_compat.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/hacs_auto_refresh.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/log_filters.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/editing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/redaction.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/renamed_tools.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/de.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/en.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/es.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/it.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/best_practice_result.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/best_practice_variables.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/bulk_selector.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_config_entries.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_identity.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/error_log_parsing.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/hacs_registration.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_security_policy.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/config_write_lock.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/entity_membership.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/visibility/enforcement.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/tests/__init__.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/tests/test_constants.py +0 -0
- {ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/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.dev2440"
|
|
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
|
|
@@ -8,11 +8,16 @@ individual translations may be incomplete and safely inherit missing values.
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
+
import logging
|
|
11
12
|
import re
|
|
12
13
|
from collections import Counter
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import Any
|
|
15
16
|
|
|
17
|
+
from . import _locale_policy
|
|
18
|
+
|
|
19
|
+
_LOGGER = logging.getLogger(__name__)
|
|
20
|
+
|
|
16
21
|
DEFAULT_LOCALE = "en"
|
|
17
22
|
LOCALE_COOKIE = "ha_mcp_locale"
|
|
18
23
|
LOCALES_DIR = Path(__file__).parent / "locales"
|
|
@@ -87,6 +92,220 @@ def _validate_tools(value: Any, *, context: str) -> dict[str, dict[str, str]]:
|
|
|
87
92
|
return result
|
|
88
93
|
|
|
89
94
|
|
|
95
|
+
def _warn_best_effort_catalog(locale: str, path: Path, exc: Exception) -> None:
|
|
96
|
+
_LOGGER.warning("Skipping best-effort locale %s from %s: %s", locale, path, exc)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _warn_best_effort_entry(locale: str, entry: str, exc: Exception) -> None:
|
|
100
|
+
_LOGGER.warning(
|
|
101
|
+
"Ignoring invalid best-effort locale %s %s; using English fallback: %s",
|
|
102
|
+
locale,
|
|
103
|
+
entry,
|
|
104
|
+
exc,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _validate_best_effort_string_map(
|
|
109
|
+
value: Any,
|
|
110
|
+
*,
|
|
111
|
+
locale: str,
|
|
112
|
+
context: str,
|
|
113
|
+
entry_kind: str,
|
|
114
|
+
) -> dict[str, str]:
|
|
115
|
+
"""Drop invalid best-effort strings while preserving valid siblings."""
|
|
116
|
+
if value is None:
|
|
117
|
+
return {}
|
|
118
|
+
if not isinstance(value, dict):
|
|
119
|
+
raise ValueError(f"{context} must be an object")
|
|
120
|
+
|
|
121
|
+
result: dict[str, str] = {}
|
|
122
|
+
for key, text in value.items():
|
|
123
|
+
try:
|
|
124
|
+
result.update(_validate_string_map({key: text}, context=context))
|
|
125
|
+
except ValueError as exc:
|
|
126
|
+
_warn_best_effort_entry(locale, f"{entry_kind} {key!r}", exc)
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _validate_best_effort_tools(
|
|
131
|
+
value: Any, *, locale: str, context: str
|
|
132
|
+
) -> dict[str, dict[str, str]]:
|
|
133
|
+
"""Drop invalid best-effort tool fields while preserving valid siblings."""
|
|
134
|
+
if value is None:
|
|
135
|
+
return {}
|
|
136
|
+
if not isinstance(value, dict):
|
|
137
|
+
raise ValueError(f"{context} must be an object")
|
|
138
|
+
|
|
139
|
+
result: dict[str, dict[str, str]] = {}
|
|
140
|
+
for tool_name, tool_values in value.items():
|
|
141
|
+
if not isinstance(tool_name, str) or not isinstance(tool_values, dict):
|
|
142
|
+
exc = ValueError(f"{context} entries must be objects keyed by tool name")
|
|
143
|
+
_warn_best_effort_entry(locale, f"tool {tool_name!r}", exc)
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
translated: dict[str, str] = {}
|
|
147
|
+
for field, field_value in tool_values.items():
|
|
148
|
+
if field not in ("title", "description"):
|
|
149
|
+
exc = ValueError(
|
|
150
|
+
f"{context}.{tool_name} has unsupported field: {field!r}"
|
|
151
|
+
)
|
|
152
|
+
_warn_best_effort_entry(
|
|
153
|
+
locale, f"tool {tool_name!r} field {field!r}", exc
|
|
154
|
+
)
|
|
155
|
+
continue
|
|
156
|
+
try:
|
|
157
|
+
validated = _validate_tools(
|
|
158
|
+
{tool_name: {field: field_value}}, context=context
|
|
159
|
+
)
|
|
160
|
+
except ValueError as exc:
|
|
161
|
+
_warn_best_effort_entry(
|
|
162
|
+
locale, f"tool {tool_name!r} field {field!r}", exc
|
|
163
|
+
)
|
|
164
|
+
continue
|
|
165
|
+
if field in validated[tool_name]:
|
|
166
|
+
translated[field] = validated[tool_name][field]
|
|
167
|
+
if translated:
|
|
168
|
+
result[tool_name] = translated
|
|
169
|
+
return result
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _load_catalog_file(
|
|
173
|
+
path: Path, *, best_effort_locale: str | None = None
|
|
174
|
+
) -> dict[str, Any]:
|
|
175
|
+
"""Load and validate one catalog without coupling it to its siblings."""
|
|
176
|
+
try:
|
|
177
|
+
raw = json.loads(path.read_text(encoding="utf-8"))
|
|
178
|
+
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
|
|
179
|
+
raise ImportError(f"Invalid settings UI locale catalog: {path}") from exc
|
|
180
|
+
if not isinstance(raw, dict):
|
|
181
|
+
raise ValueError(f"Locale catalog {path} must contain a JSON object")
|
|
182
|
+
|
|
183
|
+
meta = raw.get("meta")
|
|
184
|
+
if not isinstance(meta, dict):
|
|
185
|
+
raise ValueError(f"Locale catalog {path} must define a meta object")
|
|
186
|
+
native_name = meta.get("native_name")
|
|
187
|
+
direction = meta.get("dir", "ltr")
|
|
188
|
+
if not isinstance(native_name, str) or not native_name.strip():
|
|
189
|
+
raise ValueError(f"Locale catalog {path} needs meta.native_name")
|
|
190
|
+
if direction not in ("ltr", "rtl"):
|
|
191
|
+
raise ValueError(f"Locale catalog {path} meta.dir must be ltr or rtl")
|
|
192
|
+
|
|
193
|
+
unknown_sections = set(raw) - {"meta", "messages", "tool_groups", "tools"}
|
|
194
|
+
if unknown_sections:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"Locale catalog {path} has unsupported sections: "
|
|
197
|
+
f"{sorted(unknown_sections)}"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if best_effort_locale is None:
|
|
201
|
+
messages = _validate_string_map(
|
|
202
|
+
raw.get("messages"), context=f"{path.name}.messages"
|
|
203
|
+
)
|
|
204
|
+
tool_groups = _validate_string_map(
|
|
205
|
+
raw.get("tool_groups"), context=f"{path.name}.tool_groups"
|
|
206
|
+
)
|
|
207
|
+
tools = _validate_tools(raw.get("tools"), context=f"{path.name}.tools")
|
|
208
|
+
else:
|
|
209
|
+
messages = _validate_best_effort_string_map(
|
|
210
|
+
raw.get("messages"),
|
|
211
|
+
locale=best_effort_locale,
|
|
212
|
+
context=f"{path.name}.messages",
|
|
213
|
+
entry_kind="message",
|
|
214
|
+
)
|
|
215
|
+
tool_groups = _validate_best_effort_string_map(
|
|
216
|
+
raw.get("tool_groups"),
|
|
217
|
+
locale=best_effort_locale,
|
|
218
|
+
context=f"{path.name}.tool_groups",
|
|
219
|
+
entry_kind="tool group",
|
|
220
|
+
)
|
|
221
|
+
tools = _validate_best_effort_tools(
|
|
222
|
+
raw.get("tools"),
|
|
223
|
+
locale=best_effort_locale,
|
|
224
|
+
context=f"{path.name}.tools",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
"meta": {"native_name": native_name, "dir": direction},
|
|
229
|
+
"messages": messages,
|
|
230
|
+
"tool_groups": tool_groups,
|
|
231
|
+
"tools": tools,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _catalog_fragment(
|
|
236
|
+
catalog: dict[str, Any],
|
|
237
|
+
*,
|
|
238
|
+
messages: dict[str, str] | None = None,
|
|
239
|
+
tools: dict[str, dict[str, str]] | None = None,
|
|
240
|
+
) -> dict[str, Any]:
|
|
241
|
+
"""Return a minimal catalog used to validate one translated entry."""
|
|
242
|
+
return {
|
|
243
|
+
"meta": catalog["meta"],
|
|
244
|
+
"messages": messages or {},
|
|
245
|
+
"tool_groups": {},
|
|
246
|
+
"tools": tools or {},
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _sanitize_best_effort_catalog(
|
|
251
|
+
locale: str,
|
|
252
|
+
catalog: dict[str, Any],
|
|
253
|
+
english: dict[str, Any],
|
|
254
|
+
settings_html: Path,
|
|
255
|
+
) -> dict[str, Any]:
|
|
256
|
+
"""Drop only invalid translated entries so the rest of a locale survives."""
|
|
257
|
+
sanitized = {
|
|
258
|
+
"meta": dict(catalog["meta"]),
|
|
259
|
+
"messages": dict(catalog["messages"]),
|
|
260
|
+
"tool_groups": dict(catalog["tool_groups"]),
|
|
261
|
+
"tools": {
|
|
262
|
+
tool_name: dict(fields) for tool_name, fields in catalog["tools"].items()
|
|
263
|
+
},
|
|
264
|
+
}
|
|
265
|
+
known_panels = _known_panels(settings_html)
|
|
266
|
+
|
|
267
|
+
for key, translated in tuple(sanitized["messages"].items()):
|
|
268
|
+
english_messages = (
|
|
269
|
+
{key: english["messages"][key]} if key in english["messages"] else {}
|
|
270
|
+
)
|
|
271
|
+
candidate = {
|
|
272
|
+
DEFAULT_LOCALE: _catalog_fragment(english, messages=english_messages),
|
|
273
|
+
locale: _catalog_fragment(sanitized, messages={key: translated}),
|
|
274
|
+
}
|
|
275
|
+
try:
|
|
276
|
+
_validate_placeholder_parity(candidate)
|
|
277
|
+
_validate_inline_markup(candidate)
|
|
278
|
+
_validate_panel_links(candidate, settings_html, known_panels=known_panels)
|
|
279
|
+
except ValueError as exc:
|
|
280
|
+
del sanitized["messages"][key]
|
|
281
|
+
_warn_best_effort_entry(locale, f"message {key!r}", exc)
|
|
282
|
+
|
|
283
|
+
for tool_name, translated_tool in tuple(sanitized["tools"].items()):
|
|
284
|
+
for field, translated in tuple(translated_tool.items()):
|
|
285
|
+
english_field = english["tools"].get(tool_name, {}).get(field)
|
|
286
|
+
english_tools = (
|
|
287
|
+
{tool_name: {field: english_field}} if english_field is not None else {}
|
|
288
|
+
)
|
|
289
|
+
candidate = {
|
|
290
|
+
DEFAULT_LOCALE: _catalog_fragment(english, tools=english_tools),
|
|
291
|
+
locale: _catalog_fragment(
|
|
292
|
+
sanitized,
|
|
293
|
+
tools={tool_name: {field: translated}},
|
|
294
|
+
),
|
|
295
|
+
}
|
|
296
|
+
try:
|
|
297
|
+
_validate_placeholder_parity(candidate)
|
|
298
|
+
except ValueError as exc:
|
|
299
|
+
del translated_tool[field]
|
|
300
|
+
_warn_best_effort_entry(
|
|
301
|
+
locale, f"tool {tool_name!r} field {field!r}", exc
|
|
302
|
+
)
|
|
303
|
+
if not translated_tool:
|
|
304
|
+
del sanitized["tools"][tool_name]
|
|
305
|
+
|
|
306
|
+
return sanitized
|
|
307
|
+
|
|
308
|
+
|
|
90
309
|
def load_catalogs(
|
|
91
310
|
directory: Path = LOCALES_DIR, settings_html: Path = _SETTINGS_HTML
|
|
92
311
|
) -> dict[str, dict[str, Any]]:
|
|
@@ -106,47 +325,35 @@ def load_catalogs(
|
|
|
106
325
|
for path in paths:
|
|
107
326
|
locale = path.stem.lower().replace("_", "-")
|
|
108
327
|
try:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
meta = raw.get("meta")
|
|
116
|
-
if not isinstance(meta, dict):
|
|
117
|
-
raise ValueError(f"Locale catalog {path} must define a meta object")
|
|
118
|
-
native_name = meta.get("native_name")
|
|
119
|
-
direction = meta.get("dir", "ltr")
|
|
120
|
-
if not isinstance(native_name, str) or not native_name.strip():
|
|
121
|
-
raise ValueError(f"Locale catalog {path} needs meta.native_name")
|
|
122
|
-
if direction not in ("ltr", "rtl"):
|
|
123
|
-
raise ValueError(f"Locale catalog {path} meta.dir must be ltr or rtl")
|
|
124
|
-
|
|
125
|
-
unknown_sections = set(raw) - {"meta", "messages", "tool_groups", "tools"}
|
|
126
|
-
if unknown_sections:
|
|
127
|
-
raise ValueError(
|
|
128
|
-
f"Locale catalog {path} has unsupported sections: "
|
|
129
|
-
f"{sorted(unknown_sections)}"
|
|
328
|
+
catalogs[locale] = _load_catalog_file(
|
|
329
|
+
path,
|
|
330
|
+
best_effort_locale=(
|
|
331
|
+
locale if _locale_policy.is_best_effort_locale(locale) else None
|
|
332
|
+
),
|
|
130
333
|
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
raw.get("messages"), context=f"{path.name}.messages"
|
|
136
|
-
),
|
|
137
|
-
"tool_groups": _validate_string_map(
|
|
138
|
-
raw.get("tool_groups"), context=f"{path.name}.tool_groups"
|
|
139
|
-
),
|
|
140
|
-
"tools": _validate_tools(raw.get("tools"), context=f"{path.name}.tools"),
|
|
141
|
-
}
|
|
334
|
+
except (ImportError, ValueError) as exc:
|
|
335
|
+
if not _locale_policy.is_best_effort_locale(locale):
|
|
336
|
+
raise
|
|
337
|
+
_warn_best_effort_catalog(locale, path, exc)
|
|
142
338
|
|
|
143
339
|
if DEFAULT_LOCALE not in catalogs:
|
|
144
340
|
raise ImportError(
|
|
145
341
|
f"The settings UI requires {DEFAULT_LOCALE}.json in {directory}"
|
|
146
342
|
)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
343
|
+
|
|
344
|
+
strict_catalogs = {
|
|
345
|
+
locale: catalog
|
|
346
|
+
for locale, catalog in catalogs.items()
|
|
347
|
+
if not _locale_policy.is_best_effort_locale(locale)
|
|
348
|
+
}
|
|
349
|
+
_validate_placeholder_parity(strict_catalogs)
|
|
350
|
+
_validate_inline_markup(strict_catalogs)
|
|
351
|
+
_validate_panel_links(strict_catalogs, settings_html)
|
|
352
|
+
|
|
353
|
+
for locale in sorted(set(catalogs) - set(strict_catalogs)):
|
|
354
|
+
catalogs[locale] = _sanitize_best_effort_catalog(
|
|
355
|
+
locale, catalogs[locale], catalogs[DEFAULT_LOCALE], settings_html
|
|
356
|
+
)
|
|
150
357
|
return catalogs
|
|
151
358
|
|
|
152
359
|
|
|
@@ -234,7 +441,10 @@ def _known_panels(settings_html: Path = _SETTINGS_HTML) -> set[str]:
|
|
|
234
441
|
|
|
235
442
|
|
|
236
443
|
def _validate_panel_links(
|
|
237
|
-
catalogs: dict[str, dict[str, Any]],
|
|
444
|
+
catalogs: dict[str, dict[str, Any]],
|
|
445
|
+
settings_html: Path = _SETTINGS_HTML,
|
|
446
|
+
*,
|
|
447
|
+
known_panels: set[str] | None = None,
|
|
238
448
|
) -> None:
|
|
239
449
|
"""Reject cross-panel links that point at a tab which does not exist.
|
|
240
450
|
|
|
@@ -247,7 +457,7 @@ def _validate_panel_links(
|
|
|
247
457
|
and group labels go through ``escapeHtml``, so a link written there shows
|
|
248
458
|
as visible garbled markup rather than a dead link — wrong, but not silent.
|
|
249
459
|
"""
|
|
250
|
-
panels = _known_panels(settings_html)
|
|
460
|
+
panels = known_panels if known_panels is not None else _known_panels(settings_html)
|
|
251
461
|
english_messages = catalogs[DEFAULT_LOCALE]["messages"]
|
|
252
462
|
for locale, catalog in catalogs.items():
|
|
253
463
|
for key, value in catalog["messages"].items():
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Maintenance policy for intentionally best-effort locale catalogs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
# Klingon is a novelty translation maintained by hand. It must never consume
|
|
6
|
+
# machine-translation quota or block delivery for supported localizations.
|
|
7
|
+
BEST_EFFORT_LOCALES = frozenset({"tlh"})
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_best_effort_locale(locale: str) -> bool:
|
|
11
|
+
"""Return whether ``locale`` is isolated from hard localization gates."""
|
|
12
|
+
normalized = locale.lower().replace("_", "-")
|
|
13
|
+
return normalized in BEST_EFFORT_LOCALES
|
{ha_mcp_dev-8.3.0.dev2438 → ha_mcp_dev-8.3.0.dev2440}/src/ha_mcp/settings_ui/locales/README.md
RENAMED
|
@@ -24,6 +24,15 @@ Home Assistant language code names every file:
|
|
|
24
24
|
- `homeassistant-addon/translations/<code>.yaml` (generated)
|
|
25
25
|
- `homeassistant-addon-dev/translations/<code>.yaml` (generated)
|
|
26
26
|
|
|
27
|
+
Klingon (`tlh`) is the one best-effort exception. Its catalogs may be edited
|
|
28
|
+
manually and still ship, but the automatic translation planner never queues
|
|
29
|
+
them. Missing strings use English fallback; invalid or stale Klingon catalogs
|
|
30
|
+
produce warnings instead of failing CI or the daily locale sync. The runtime
|
|
31
|
+
loader skips an invalid Klingon settings catalog, and the generator can project
|
|
32
|
+
English into its add-on catalogs, so Klingon cannot prevent any other locale
|
|
33
|
+
from loading or updating. All other language codes remain subject to every
|
|
34
|
+
hard gate below.
|
|
35
|
+
|
|
27
36
|
Add the two authored catalogs, then run `python scripts/generate_locales.py`
|
|
28
37
|
and merge: the post-merge `locale-sync.yml` workflow machine-fills every string
|
|
29
38
|
over its next daily runs. The component catalog may start as an empty object;
|