ha-mcp-dev 7.12.3.dev1949__tar.gz → 7.13.0.dev1955__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ha_mcp_dev-7.12.3.dev1949/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.13.0.dev1955}/PKG-INFO +1 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/pyproject.toml +1 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/rest_client.py +4 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/config.py +21 -5
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_config.py +2 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_deep.py +3 -5
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_entities.py +10 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_fetch.py +18 -21
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_scenes.py +1 -2
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_blueprints.py +119 -33
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/LICENSE +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/README.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/setup.cfg +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/tests/test_env_manager.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ha-mcp-dev"
|
|
7
|
-
version = "7.
|
|
7
|
+
version = "7.13.0.dev1955"
|
|
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
|
|
@@ -1453,7 +1453,10 @@ class HomeAssistantClient:
|
|
|
1453
1453
|
],
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
|
-
|
|
1456
|
+
# Name the command in the log line: a bare "Command failed:
|
|
1457
|
+
# Unknown command." is undiagnosable from a user's log
|
|
1458
|
+
# (issue #1889 took a live reproduction to attribute).
|
|
1459
|
+
logger.error(f"WebSocket message failed ({message.get('type')}): {e}")
|
|
1457
1460
|
return {"success": False, "error": str(e)}
|
|
1458
1461
|
|
|
1459
1462
|
return {"success": False, "error": "WebSocket request failed"}
|
|
@@ -1440,10 +1440,12 @@ _settings: Settings | None = None
|
|
|
1440
1440
|
# THROUGH THIS DICT — never
|
|
1441
1441
|
# via os.environ — so the admin token can't be read from the shared HA process
|
|
1442
1442
|
# environment. Applied onto the Settings singleton in ``get_global_settings``.
|
|
1443
|
-
_EMBEDDED_CONNECTION: dict[str, str] = {}
|
|
1443
|
+
_EMBEDDED_CONNECTION: dict[str, str | bool] = {}
|
|
1444
1444
|
|
|
1445
1445
|
|
|
1446
|
-
def set_embedded_connection(
|
|
1446
|
+
def set_embedded_connection(
|
|
1447
|
+
url: str, token: str, verify_ssl: bool | None = None
|
|
1448
|
+
) -> None:
|
|
1447
1449
|
"""Register the in-process HA connection for embedded mode.
|
|
1448
1450
|
|
|
1449
1451
|
Embedded-mode only: called by the ha_mcp_tools custom component's in-process
|
|
@@ -1459,9 +1461,20 @@ def set_embedded_connection(url: str, token: str) -> None:
|
|
|
1459
1461
|
Registration therefore cannot assume it runs before the first build — the
|
|
1460
1462
|
integration imports this function from the very package whose import
|
|
1461
1463
|
creates the singleton.
|
|
1464
|
+
|
|
1465
|
+
``verify_ssl`` lets the component disable certificate verification when it
|
|
1466
|
+
derives an ``https://127.0.0.1`` loopback URL from Home Assistant's SSL
|
|
1467
|
+
config (issue #1890): HA's certificate is issued for its hostname, never
|
|
1468
|
+
for 127.0.0.1, so verification on the loopback connection can only fail.
|
|
1469
|
+
``None`` (the default, and what pre-#1890 components pass implicitly)
|
|
1470
|
+
leaves ``Settings.verify_ssl`` alone.
|
|
1462
1471
|
"""
|
|
1463
1472
|
_EMBEDDED_CONNECTION["url"] = url
|
|
1464
1473
|
_EMBEDDED_CONNECTION["token"] = token
|
|
1474
|
+
if verify_ssl is None:
|
|
1475
|
+
_EMBEDDED_CONNECTION.pop("verify_ssl", None)
|
|
1476
|
+
else:
|
|
1477
|
+
_EMBEDDED_CONNECTION["verify_ssl"] = verify_ssl
|
|
1465
1478
|
if _settings is not None:
|
|
1466
1479
|
_apply_embedded_connection(_settings)
|
|
1467
1480
|
|
|
@@ -1764,7 +1777,7 @@ def _apply_backup_overrides(settings: "Settings") -> None:
|
|
|
1764
1777
|
|
|
1765
1778
|
|
|
1766
1779
|
def _apply_embedded_connection(settings: "Settings") -> None:
|
|
1767
|
-
"""Apply the in-process embedded HA connection (url/token) if registered.
|
|
1780
|
+
"""Apply the in-process embedded HA connection (url/token/verify_ssl) if registered.
|
|
1768
1781
|
|
|
1769
1782
|
No-op outside embedded mode. Plain ``setattr`` (``validate_assignment`` is off
|
|
1770
1783
|
on ``Settings``, mirroring ``_apply_backup_overrides``), so the loopback URL
|
|
@@ -1773,10 +1786,13 @@ def _apply_embedded_connection(settings: "Settings") -> None:
|
|
|
1773
1786
|
"""
|
|
1774
1787
|
url = _EMBEDDED_CONNECTION.get("url")
|
|
1775
1788
|
token = _EMBEDDED_CONNECTION.get("token")
|
|
1776
|
-
if url:
|
|
1789
|
+
if isinstance(url, str) and url:
|
|
1777
1790
|
settings.homeassistant_url = url.rstrip("/")
|
|
1778
|
-
if token:
|
|
1791
|
+
if isinstance(token, str) and token:
|
|
1779
1792
|
settings.homeassistant_token = token
|
|
1793
|
+
verify_ssl = _EMBEDDED_CONNECTION.get("verify_ssl")
|
|
1794
|
+
if isinstance(verify_ssl, bool):
|
|
1795
|
+
settings.verify_ssl = verify_ssl
|
|
1780
1796
|
|
|
1781
1797
|
|
|
1782
1798
|
def get_global_settings() -> Settings:
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_config.py
RENAMED
|
@@ -15,7 +15,8 @@ BULK_REST_TIMEOUT = 5.0 # Timeout for bulk REST endpoint calls
|
|
|
15
15
|
BULK_WEBSOCKET_TIMEOUT = 3.0 # Timeout for bulk WebSocket calls
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
# Attempt-C (per-id fallback
|
|
18
|
+
# Attempt-C (per-id fallback; the letter predates the removal of the phantom
|
|
19
|
+
# WS bulk tier in #1889 and is kept as a proper name) tuning knobs. Sourced from the resolved
|
|
19
20
|
# Settings (issues #1538 / #1784) so the env var, the web Settings UI
|
|
20
21
|
# override file, and the field defaults all flow through one precedence path
|
|
21
22
|
# — and so add-on users (who cannot set raw env vars) can tune them from the
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_deep.py
RENAMED
|
@@ -319,7 +319,7 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
319
319
|
*,
|
|
320
320
|
config_time_budget: float | None = None,
|
|
321
321
|
) -> tuple[list[dict[str, Any]], int, int, int, int]:
|
|
322
|
-
"""Deep-search automations:
|
|
322
|
+
"""Deep-search automations: two-tier config fetch (REST bulk -> budgeted individual).
|
|
323
323
|
|
|
324
324
|
Returns ``(matches, skipped_count, failed_count, yaml_skipped_count,
|
|
325
325
|
timeout_count)``. ``skipped_count`` is non-zero only when bulk fetch
|
|
@@ -360,10 +360,9 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
360
360
|
)
|
|
361
361
|
)
|
|
362
362
|
|
|
363
|
-
# Phase 2: bulk fetch (
|
|
363
|
+
# Phase 2: bulk fetch (REST)
|
|
364
364
|
configs = await self._bulk_fetch_configs(
|
|
365
365
|
"/config/automation/config",
|
|
366
|
-
["config/automation/config/list", "automation/config/list"],
|
|
367
366
|
lambda item: item.get("id"),
|
|
368
367
|
BULK_REST_TIMEOUT,
|
|
369
368
|
"Automation",
|
|
@@ -474,7 +473,7 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
474
473
|
*,
|
|
475
474
|
config_time_budget: float | None = None,
|
|
476
475
|
) -> tuple[list[dict[str, Any]], int, int, int, int]:
|
|
477
|
-
"""Deep-search scripts: same
|
|
476
|
+
"""Deep-search scripts: same two-tier strategy as automations.
|
|
478
477
|
|
|
479
478
|
Returns ``(matches, skipped_count, failed_count, yaml_skipped_count,
|
|
480
479
|
timeout_count)``; semantics identical to ``_deep_search_automations``
|
|
@@ -500,7 +499,6 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
500
499
|
# Phase 2: bulk fetch
|
|
501
500
|
configs = await self._bulk_fetch_configs(
|
|
502
501
|
"/config/script/config",
|
|
503
|
-
["config/script/config/list", "script/config/list"],
|
|
504
502
|
lambda item: (
|
|
505
503
|
item.get("id") or item.get("alias", "").lower().replace(" ", "_")
|
|
506
504
|
),
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_entities.py
RENAMED
|
@@ -221,7 +221,16 @@ class EntitySearchMixin(_SearchBase):
|
|
|
221
221
|
if isinstance(entries_resp, dict) and entries_resp.get("success"):
|
|
222
222
|
for eid, entry in (entries_resp.get("result", {}) or {}).items():
|
|
223
223
|
if isinstance(entry, dict):
|
|
224
|
-
|
|
224
|
+
# String entries only: HA serializes the
|
|
225
|
+
# COMPUTED_NAME alias sentinel ("the computed
|
|
226
|
+
# entity name is an alias") as null over the
|
|
227
|
+
# websocket API; ingesting it would put None in
|
|
228
|
+
# every downstream alias string op.
|
|
229
|
+
aliases_map[eid] = [
|
|
230
|
+
a
|
|
231
|
+
for a in (entry.get("aliases") or [])
|
|
232
|
+
if isinstance(a, str)
|
|
233
|
+
]
|
|
225
234
|
else:
|
|
226
235
|
failed_chunks += 1
|
|
227
236
|
logger.warning(
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_fetch.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Shared
|
|
1
|
+
"""Shared two-tier config fetching (bulk REST, then budgeted per-id) for deep search."""
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
@@ -8,7 +8,7 @@ from typing import Any
|
|
|
8
8
|
|
|
9
9
|
import httpx
|
|
10
10
|
|
|
11
|
-
from ._config import
|
|
11
|
+
from ._config import INDIVIDUAL_FETCH_BATCH_SIZE
|
|
12
12
|
from ._scoring import ScoringMixin
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
@@ -35,7 +35,7 @@ def is_timeout_error(exc: BaseException) -> bool:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
class ConfigFetchMixin(ScoringMixin):
|
|
38
|
-
"""REST
|
|
38
|
+
"""REST bulk + budgeted individual config fetch, and scoring of fetched entries."""
|
|
39
39
|
|
|
40
40
|
@staticmethod
|
|
41
41
|
def _index_configs(
|
|
@@ -53,17 +53,25 @@ class ConfigFetchMixin(ScoringMixin):
|
|
|
53
53
|
async def _bulk_fetch_configs(
|
|
54
54
|
self,
|
|
55
55
|
rest_endpoint: str,
|
|
56
|
-
ws_types: list[str],
|
|
57
56
|
id_of: Callable[[dict[str, Any]], str | None],
|
|
58
57
|
rest_timeout: float,
|
|
59
58
|
label: str,
|
|
60
59
|
) -> dict[str, dict[str, Any]] | None:
|
|
61
|
-
"""Bulk-fetch all configs of one domain
|
|
62
|
-
|
|
63
|
-
Returns ``{id: config}`` (possibly empty) on
|
|
64
|
-
|
|
65
|
-
REST list returns ``{}`` (not
|
|
66
|
-
individual-fetch fallback exactly as
|
|
60
|
+
"""Bulk-fetch all configs of one domain from its REST endpoint.
|
|
61
|
+
|
|
62
|
+
Returns ``{id: config}`` (possibly empty) on success, or ``None`` when
|
|
63
|
+
the fetch failed (the caller then falls back to budgeted individual
|
|
64
|
+
fetches). An empty-but-successful REST list returns ``{}`` (not
|
|
65
|
+
``None``) so the caller skips the individual-fetch fallback exactly as
|
|
66
|
+
it would for a populated response.
|
|
67
|
+
|
|
68
|
+
There is deliberately NO WebSocket fallback here: the
|
|
69
|
+
``config/<domain>/config/list`` and ``<domain>/config/list`` command
|
|
70
|
+
types this used to try have never existed in HA core, so every legacy
|
|
71
|
+
config search fired paired ``unknown_command`` rejections that
|
|
72
|
+
``send_websocket_message`` logged as ERRORs — the recurring
|
|
73
|
+
"WebSocket message failed: Command failed: Unknown command." log spam
|
|
74
|
+
of issue #1889.
|
|
67
75
|
"""
|
|
68
76
|
try:
|
|
69
77
|
resp = await asyncio.wait_for(
|
|
@@ -74,17 +82,6 @@ class ConfigFetchMixin(ScoringMixin):
|
|
|
74
82
|
return self._index_configs(resp, id_of)
|
|
75
83
|
except Exception as e:
|
|
76
84
|
logger.debug(f"{label} REST bulk fetch failed: {e}")
|
|
77
|
-
|
|
78
|
-
for ws_type in ws_types:
|
|
79
|
-
try:
|
|
80
|
-
ws_resp = await asyncio.wait_for(
|
|
81
|
-
self.client.send_websocket_message({"type": ws_type}),
|
|
82
|
-
timeout=BULK_WEBSOCKET_TIMEOUT,
|
|
83
|
-
)
|
|
84
|
-
if isinstance(ws_resp, dict) and ws_resp.get("success"):
|
|
85
|
-
return self._index_configs(ws_resp.get("result", []), id_of)
|
|
86
|
-
except Exception as e:
|
|
87
|
-
logger.debug(f"{label} WebSocket bulk fetch ({ws_type}) failed: {e}")
|
|
88
85
|
return None
|
|
89
86
|
|
|
90
87
|
async def _individual_fetch_budgeted(
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_scenes.py
RENAMED
|
@@ -212,7 +212,7 @@ class SceneSearchMixin(ConfigFetchMixin):
|
|
|
212
212
|
config_time_budget: float | None = None,
|
|
213
213
|
prefetched_registry: Any = None,
|
|
214
214
|
) -> tuple[list[dict[str, Any]], int, int, int, bool, int]:
|
|
215
|
-
"""Deep-search scenes:
|
|
215
|
+
"""Deep-search scenes: two-tier strategy plus registry-walk augmentation.
|
|
216
216
|
|
|
217
217
|
Scenes have no listing primitive, so entities are enumerated from
|
|
218
218
|
get_states() and configs fetched per id. Returns the scene results plus
|
|
@@ -239,7 +239,6 @@ class SceneSearchMixin(ConfigFetchMixin):
|
|
|
239
239
|
# Phase 2: bulk fetch
|
|
240
240
|
configs = await self._bulk_fetch_configs(
|
|
241
241
|
"/config/scene/config",
|
|
242
|
-
["config/scene/config/list", "scene/config/list"],
|
|
243
242
|
lambda item: (
|
|
244
243
|
item.get("id") or item.get("name", "").lower().replace(" ", "_")
|
|
245
244
|
),
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_blueprints.py
RENAMED
|
@@ -222,6 +222,68 @@ class BlueprintTools:
|
|
|
222
222
|
)
|
|
223
223
|
return None # unreachable: exception_to_structured_error always raises
|
|
224
224
|
|
|
225
|
+
async def _save_blueprint(
|
|
226
|
+
self,
|
|
227
|
+
url: str,
|
|
228
|
+
domain: str,
|
|
229
|
+
path: str,
|
|
230
|
+
yaml_data: str,
|
|
231
|
+
overwrite: bool,
|
|
232
|
+
) -> dict[str, Any]:
|
|
233
|
+
"""Persist a validated blueprint via blueprint/save, raising on failure.
|
|
234
|
+
|
|
235
|
+
Returns the blueprint/save result payload (contains overrides_existing).
|
|
236
|
+
"""
|
|
237
|
+
save_message: dict[str, Any] = {
|
|
238
|
+
"type": "blueprint/save",
|
|
239
|
+
"domain": domain,
|
|
240
|
+
"path": path,
|
|
241
|
+
"yaml": yaml_data,
|
|
242
|
+
"source_url": url,
|
|
243
|
+
}
|
|
244
|
+
# allow_override only exists on HA >= 2023.12 and the WS schema
|
|
245
|
+
# rejects unknown keys - only send it when actually overwriting
|
|
246
|
+
if overwrite:
|
|
247
|
+
save_message["allow_override"] = True
|
|
248
|
+
|
|
249
|
+
save_response = await self._client.send_websocket_message(save_message)
|
|
250
|
+
|
|
251
|
+
if not save_response.get("success"):
|
|
252
|
+
error = save_response.get("error", {})
|
|
253
|
+
save_error = (
|
|
254
|
+
error.get("message", str(error))
|
|
255
|
+
if isinstance(error, dict)
|
|
256
|
+
else str(error)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
suggestions = [
|
|
260
|
+
"The blueprint was validated but could not be saved to disk",
|
|
261
|
+
"Use ha_get_blueprint() to check if it already exists",
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
# Reachable despite the early exists check: a race between
|
|
265
|
+
# import and save, or an installed file that failed to load
|
|
266
|
+
# (core reports exists=false for those)
|
|
267
|
+
already_exists = "already exists" in save_error.lower()
|
|
268
|
+
if already_exists:
|
|
269
|
+
suggestions.insert(
|
|
270
|
+
0,
|
|
271
|
+
"A blueprint with this path already exists - pass overwrite=true to re-import it",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
raise_tool_error(
|
|
275
|
+
create_error_response(
|
|
276
|
+
ErrorCode.RESOURCE_ALREADY_EXISTS
|
|
277
|
+
if already_exists
|
|
278
|
+
else ErrorCode.SERVICE_CALL_FAILED,
|
|
279
|
+
save_error,
|
|
280
|
+
context={"url": url, "path": path},
|
|
281
|
+
suggestions=suggestions,
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return save_response.get("result") or {}
|
|
286
|
+
|
|
225
287
|
@tool(
|
|
226
288
|
name="ha_import_blueprint",
|
|
227
289
|
tags={"Blueprints"},
|
|
@@ -240,17 +302,29 @@ class BlueprintTools:
|
|
|
240
302
|
description="URL to import blueprint from (GitHub, Home Assistant Community, or direct YAML URL)"
|
|
241
303
|
),
|
|
242
304
|
],
|
|
305
|
+
overwrite: Annotated[
|
|
306
|
+
bool,
|
|
307
|
+
Field(
|
|
308
|
+
description="Overwrite the blueprint if it is already installed (re-import). "
|
|
309
|
+
"Home Assistant reloads all automations/scripts using the blueprint.",
|
|
310
|
+
default=False,
|
|
311
|
+
),
|
|
312
|
+
] = False,
|
|
243
313
|
) -> dict[str, Any]:
|
|
244
314
|
"""
|
|
245
315
|
Import a blueprint from a URL.
|
|
246
316
|
|
|
247
317
|
Imports a blueprint from GitHub, Home Assistant Community forums,
|
|
248
|
-
or any direct URL to a blueprint YAML file.
|
|
318
|
+
or any direct URL to a blueprint YAML file. Set overwrite=true to
|
|
319
|
+
re-import a blueprint that is already installed (equivalent to the
|
|
320
|
+
UI's "Re-import blueprint" action) - Home Assistant then reloads all
|
|
321
|
+
automations/scripts that use it.
|
|
249
322
|
|
|
250
323
|
EXAMPLES:
|
|
251
324
|
- Import from GitHub: ha_import_blueprint("https://github.com/user/repo/blob/main/blueprint.yaml")
|
|
252
325
|
- Import from HA Community: ha_import_blueprint("https://community.home-assistant.io/t/motion-light/123456")
|
|
253
326
|
- Import direct YAML: ha_import_blueprint("https://example.com/my-blueprint.yaml")
|
|
327
|
+
- Re-import an installed blueprint: ha_import_blueprint("https://example.com/my-blueprint.yaml", overwrite=True)
|
|
254
328
|
|
|
255
329
|
SUPPORTED SOURCES:
|
|
256
330
|
- GitHub repository URLs (will be converted to raw URLs)
|
|
@@ -260,6 +334,7 @@ class BlueprintTools:
|
|
|
260
334
|
RETURNS:
|
|
261
335
|
- Import result with the blueprint path where it was saved
|
|
262
336
|
- Blueprint metadata (name, domain, description)
|
|
337
|
+
- overrides_existing: true when an installed blueprint was overwritten
|
|
263
338
|
- Error details if import fails
|
|
264
339
|
"""
|
|
265
340
|
try:
|
|
@@ -328,43 +403,50 @@ class BlueprintTools:
|
|
|
328
403
|
if not suggested_filename.endswith((".yaml", ".yml")):
|
|
329
404
|
suggested_filename = suggested_filename + ".yaml"
|
|
330
405
|
|
|
331
|
-
#
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
else str(error)
|
|
406
|
+
# blueprint/save does not re-run these checks (currently the
|
|
407
|
+
# blueprint's min Home Assistant version) - without this gate an
|
|
408
|
+
# unsupported blueprint saves cleanly and reports success
|
|
409
|
+
validation_errors = result_data.get("validation_errors")
|
|
410
|
+
if validation_errors:
|
|
411
|
+
raise_tool_error(
|
|
412
|
+
create_error_response(
|
|
413
|
+
ErrorCode.VALIDATION_FAILED,
|
|
414
|
+
"Blueprint failed validation: "
|
|
415
|
+
+ "; ".join(str(e) for e in validation_errors),
|
|
416
|
+
context={"url": url, "validation_errors": validation_errors},
|
|
417
|
+
suggestions=[
|
|
418
|
+
"The blueprint is not compatible with this Home Assistant installation",
|
|
419
|
+
"Update Home Assistant to satisfy the blueprint's minimum version requirement",
|
|
420
|
+
],
|
|
421
|
+
)
|
|
348
422
|
)
|
|
349
423
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if "already exists" in save_error.lower():
|
|
356
|
-
suggestions.insert(0, "A blueprint with this path already exists")
|
|
357
|
-
|
|
424
|
+
# blueprint/import reports whether the target path is already
|
|
425
|
+
# installed - fail early with a re-import hint instead of letting
|
|
426
|
+
# blueprint/save reject the write
|
|
427
|
+
if result_data.get("exists") and not overwrite:
|
|
358
428
|
raise_tool_error(
|
|
359
429
|
create_error_response(
|
|
360
|
-
ErrorCode.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
430
|
+
ErrorCode.RESOURCE_ALREADY_EXISTS,
|
|
431
|
+
f"Blueprint already exists at '{suggested_filename}'. "
|
|
432
|
+
"Pass overwrite=true to re-import it.",
|
|
433
|
+
context={
|
|
434
|
+
"url": url,
|
|
435
|
+
"path": suggested_filename,
|
|
436
|
+
"domain": domain,
|
|
437
|
+
},
|
|
438
|
+
suggestions=[
|
|
439
|
+
"Call ha_import_blueprint with overwrite=true to update the installed blueprint",
|
|
440
|
+
"Use ha_get_blueprint() to inspect the currently installed version",
|
|
441
|
+
],
|
|
364
442
|
)
|
|
365
443
|
)
|
|
366
444
|
|
|
367
|
-
|
|
445
|
+
# Save the blueprint to disk (blueprint/import only validates)
|
|
446
|
+
save_result = await self._save_blueprint(
|
|
447
|
+
url, domain, suggested_filename, raw_data, overwrite
|
|
448
|
+
)
|
|
449
|
+
overrides_existing = save_result.get("overrides_existing", False)
|
|
368
450
|
|
|
369
451
|
return {
|
|
370
452
|
"success": True,
|
|
@@ -375,8 +457,12 @@ class BlueprintTools:
|
|
|
375
457
|
"name": blueprint_meta.get("name"),
|
|
376
458
|
"description": blueprint_meta.get("description"),
|
|
377
459
|
},
|
|
378
|
-
"overrides_existing":
|
|
379
|
-
"message":
|
|
460
|
+
"overrides_existing": overrides_existing,
|
|
461
|
+
"message": (
|
|
462
|
+
"Blueprint re-imported successfully. Automations/scripts using it were reloaded."
|
|
463
|
+
if overrides_existing
|
|
464
|
+
else "Blueprint imported successfully. Use ha_get_blueprint() to see all installed blueprints."
|
|
465
|
+
),
|
|
380
466
|
}
|
|
381
467
|
|
|
382
468
|
except ToolError:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/supervisor_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/websocket_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/client/websocket_listener.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/content.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/paths.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/dashboard_screenshot/provision.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_advanced.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_backups.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_fs.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_server.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_theme.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_handlers_tools.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_persistence.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_supervisor.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/_tools_meta.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/settings_ui/settings.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/stdio_settings_sidecar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/config_entry_flow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/reference_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_base.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_overview.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/smart_search/_scoring.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_bug_report.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_categories.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_scenes.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_filesystem.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_integrations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/tools_yaml_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/tools/validation_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/transforms/lite_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/utils/operation_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp/visibility/persistence.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/requires.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.12.3.dev1949 → ha_mcp_dev-7.13.0.dev1955}/src/ha_mcp_dev.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|