ha-mcp-dev 7.8.1.dev730__tar.gz → 7.8.1.dev731__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.8.1.dev730/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.8.1.dev731}/PKG-INFO +1 -1
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/pyproject.toml +1 -1
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/backup.py +122 -15
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/LICENSE +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/README.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/setup.cfg +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/settings.css +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/settings.js +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/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.8.1.
|
|
7
|
+
version = "7.8.1.dev731"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
|
@@ -52,6 +52,12 @@ logger = logging.getLogger(__name__)
|
|
|
52
52
|
# happy-path wait noticeable.
|
|
53
53
|
_BACKUP_MAX_WAIT_S = 300
|
|
54
54
|
_BACKUP_POLL_INTERVAL_S = 2
|
|
55
|
+
# The pre-restore safety backup is a *full* backup (include_database=True, plus
|
|
56
|
+
# all add-ons on Supervised), unlike the fast create_backup path. A multi-GB
|
|
57
|
+
# full backup on constrained hardware (~2.1 GB on a Pi 5, #1681) can run well
|
|
58
|
+
# past the fast-backup window, so its completion poll gets a larger budget —
|
|
59
|
+
# timing out here aborts the restore and the retry spawns another full backup.
|
|
60
|
+
_SAFETY_BACKUP_MAX_WAIT_S = 1800
|
|
55
61
|
# Clock-skew tolerance when filtering backup entries by date vs job-start.
|
|
56
62
|
_BACKUP_DATE_FILTER_TOLERANCE_S = 5
|
|
57
63
|
|
|
@@ -540,17 +546,23 @@ async def _create_safety_backup(
|
|
|
540
546
|
ws_client: HomeAssistantWebSocketClient,
|
|
541
547
|
password: str | None,
|
|
542
548
|
agent_id: str,
|
|
543
|
-
) -> str | None:
|
|
544
|
-
"""Create a pre-restore safety backup.
|
|
549
|
+
) -> tuple[str | None, list[str]]:
|
|
550
|
+
"""Create a pre-restore safety backup and wait for it to complete.
|
|
545
551
|
|
|
546
552
|
``agent_id`` is the local backup agent (Supervisor's ``hassio.local`` or
|
|
547
553
|
Core's ``backup.local``) discovered by the caller.
|
|
548
554
|
|
|
549
|
-
|
|
550
|
-
|
|
555
|
+
Blocks until the safety backup finishes. HA's ``backup/generate`` returns
|
|
556
|
+
on job initiation, not completion, so waiting here lets the caller issue
|
|
557
|
+
``backup/restore`` afterwards without colliding with a still-running
|
|
558
|
+
backup. Returns ``(job_id, warnings)`` — ``job_id`` is None when password
|
|
559
|
+
is None (backup intentionally skipped); ``warnings`` carries any late-
|
|
560
|
+
completion notice from the poll so the caller can surface it before the
|
|
561
|
+
destructive restore. Raises ToolError if the backup fails to start or does
|
|
562
|
+
not complete in time.
|
|
551
563
|
"""
|
|
552
564
|
if password is None:
|
|
553
|
-
return None
|
|
565
|
+
return None, []
|
|
554
566
|
|
|
555
567
|
now = datetime.now()
|
|
556
568
|
safety_backup_name = f"PreRestore_Safety_{now.strftime('%Y-%m-%d_%H:%M:%S')}"
|
|
@@ -579,8 +591,45 @@ async def _create_safety_backup(
|
|
|
579
591
|
)
|
|
580
592
|
|
|
581
593
|
safety_backup_id = safety_backup.get("result", {}).get("backup_job_id")
|
|
582
|
-
logger.info(f"Safety backup
|
|
583
|
-
|
|
594
|
+
logger.info(f"Safety backup started: {safety_backup_id}, waiting for completion...")
|
|
595
|
+
|
|
596
|
+
# Wait for the safety backup to finish before returning. HA's
|
|
597
|
+
# backup/generate WS command returns as soon as the job is *initiated*,
|
|
598
|
+
# not when it completes, and the backup manager rejects any new operation
|
|
599
|
+
# while a backup is running ("Backup manager busy: create_backup"). If the
|
|
600
|
+
# caller issued backup/restore right after this returned, it would collide
|
|
601
|
+
# with the safety backup this same call just started – a self-induced
|
|
602
|
+
# deadlock (#1681). Reuse the same completion poll create_backup already
|
|
603
|
+
# uses rather than adding a second wait mechanism.
|
|
604
|
+
poll_result = await _poll_backup_completion(
|
|
605
|
+
ws_client,
|
|
606
|
+
safety_backup_name,
|
|
607
|
+
cast(str, safety_backup_id),
|
|
608
|
+
max_wait_seconds=_SAFETY_BACKUP_MAX_WAIT_S,
|
|
609
|
+
poll_interval=_BACKUP_POLL_INTERVAL_S,
|
|
610
|
+
agent_id=agent_id,
|
|
611
|
+
)
|
|
612
|
+
logger.info(f"Safety backup completed: {safety_backup_id}")
|
|
613
|
+
return cast(str, safety_backup_id), poll_result.get("warnings", [])
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _backup_protected(entry: dict[str, Any]) -> bool | None:
|
|
617
|
+
"""Whether a ``backup/info`` entry is encrypted.
|
|
618
|
+
|
|
619
|
+
``protected`` is a per-agent field (AgentBackupStatus), not top-level. A
|
|
620
|
+
backup is encrypted as a whole, so any agent reporting it makes the backup
|
|
621
|
+
protected; returns None when the ``agents`` map is absent/malformed or no
|
|
622
|
+
agent reports the flag (unknown rather than "unprotected").
|
|
623
|
+
"""
|
|
624
|
+
agents = entry.get("agents")
|
|
625
|
+
if not isinstance(agents, dict):
|
|
626
|
+
return None
|
|
627
|
+
flags = [
|
|
628
|
+
a.get("protected")
|
|
629
|
+
for a in agents.values()
|
|
630
|
+
if isinstance(a, dict) and a.get("protected") is not None
|
|
631
|
+
]
|
|
632
|
+
return any(flags) if flags else None
|
|
584
633
|
|
|
585
634
|
|
|
586
635
|
async def restore_backup(
|
|
@@ -594,7 +643,12 @@ async def restore_backup(
|
|
|
594
643
|
Args:
|
|
595
644
|
client: Home Assistant REST client
|
|
596
645
|
backup_id: Backup ID to restore
|
|
597
|
-
restore_database: Whether to restore database (historical data)
|
|
646
|
+
restore_database: Whether to restore database (historical data).
|
|
647
|
+
On Supervised installs this is reconciled against the target
|
|
648
|
+
backup's ``database_included`` flag, which Supervisor requires to
|
|
649
|
+
match when restoring Home Assistant; a warning is returned if the
|
|
650
|
+
value is overridden. On Core installs the caller's value is honoured
|
|
651
|
+
as-is (Core enforces no such match).
|
|
598
652
|
|
|
599
653
|
Returns:
|
|
600
654
|
Dictionary with restore result including safety_backup_id, status, etc.
|
|
@@ -627,9 +681,9 @@ async def restore_backup(
|
|
|
627
681
|
)
|
|
628
682
|
|
|
629
683
|
backups = backup_info.get("result", {}).get("backups", [])
|
|
630
|
-
|
|
684
|
+
matched = next((b for b in backups if b.get("backup_id") == backup_id), None)
|
|
631
685
|
|
|
632
|
-
if
|
|
686
|
+
if matched is None:
|
|
633
687
|
raise_tool_error(
|
|
634
688
|
create_error_response(
|
|
635
689
|
ErrorCode.RESOURCE_NOT_FOUND,
|
|
@@ -655,17 +709,57 @@ async def restore_backup(
|
|
|
655
709
|
logger.warning("No default password - proceeding without safety backup")
|
|
656
710
|
password = None
|
|
657
711
|
|
|
658
|
-
safety_backup_id = await _create_safety_backup(
|
|
712
|
+
safety_backup_id, safety_warnings = await _create_safety_backup(
|
|
713
|
+
ws_client, password, local_agent
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# `backup/info` returns ManagerBackup entries: `database_included` is a
|
|
717
|
+
# top-level field (inherited from BaseBackup), but `protected` is NOT —
|
|
718
|
+
# it lives per-agent under the entry's `agents` map (AgentBackupStatus),
|
|
719
|
+
# so a top-level `matched.get("protected")` is always None against real
|
|
720
|
+
# HA. Derive it from the agents map (shared with _summarize_backup).
|
|
721
|
+
target_protected = _backup_protected(matched)
|
|
722
|
+
|
|
723
|
+
# Reconcile restore_database with the target only on Supervised. HA's
|
|
724
|
+
# Supervisor raises "Restore database must match backup" when
|
|
725
|
+
# restore_homeassistant is set and restore_database != the backup's
|
|
726
|
+
# database_included flag (hassio/backup.py). HA Core's restore path
|
|
727
|
+
# (CoreBackupReaderWriter) has no such constraint and writes the caller's
|
|
728
|
+
# value verbatim, so overriding it there would silently discard an
|
|
729
|
+
# explicit request for no HA-side reason. Fall back to the caller's
|
|
730
|
+
# request when not Supervised or when the field is absent. Surface a
|
|
731
|
+
# warning when the derived value overrides what the caller asked for so
|
|
732
|
+
# the override isn't silent on a destructive op.
|
|
733
|
+
is_supervised = local_agent == "hassio.local"
|
|
734
|
+
target_database_included = matched.get("database_included")
|
|
735
|
+
if is_supervised and target_database_included is not None:
|
|
736
|
+
effective_restore_database = target_database_included
|
|
737
|
+
else:
|
|
738
|
+
effective_restore_database = restore_database
|
|
659
739
|
|
|
660
740
|
# Perform restore
|
|
661
|
-
restore_params = {
|
|
741
|
+
restore_params: dict[str, Any] = {
|
|
662
742
|
"backup_id": backup_id,
|
|
663
743
|
"agent_id": local_agent,
|
|
664
|
-
"restore_database":
|
|
744
|
+
"restore_database": effective_restore_database,
|
|
665
745
|
"restore_homeassistant": True,
|
|
666
746
|
"restore_addons": [], # Restore all addons from backup
|
|
667
747
|
"restore_folders": [], # Restore all folders from backup
|
|
668
748
|
}
|
|
749
|
+
# Forward the default backup password ONLY for a protected (encrypted)
|
|
750
|
+
# target. `password` here is HA's default create_backup.password, which
|
|
751
|
+
# is independent of whether *this* backup is encrypted: HA validates the
|
|
752
|
+
# password against the target unconditionally and rejects a password on
|
|
753
|
+
# an unprotected backup ("Invalid password for backup" →
|
|
754
|
+
# IncorrectPasswordError). Gate on the target's per-agent `protected`
|
|
755
|
+
# flag (read above) so an unprotected snapshot still restores on a
|
|
756
|
+
# default-password instance. HA's backup/restore schema types `password`
|
|
757
|
+
# as `str` (not `str | None`), so only include the key when we actually
|
|
758
|
+
# forward one — passing None would fail voluptuous validation. Without
|
|
759
|
+
# this a protected backup cannot be restored even though the HA UI,
|
|
760
|
+
# which applies the stored key, succeeds (#1681).
|
|
761
|
+
if password is not None and target_protected:
|
|
762
|
+
restore_params["password"] = password
|
|
669
763
|
|
|
670
764
|
result = await ws_client.send_command("backup/restore", **restore_params)
|
|
671
765
|
|
|
@@ -678,6 +772,18 @@ async def restore_backup(
|
|
|
678
772
|
warnings = [
|
|
679
773
|
"Home Assistant is restarting. Connection will be temporarily lost."
|
|
680
774
|
]
|
|
775
|
+
# Surface any late-completion notice from the safety-backup poll —
|
|
776
|
+
# a slow backup subsystem right before a destructive restore is
|
|
777
|
+
# exactly the signal a caller wants to see.
|
|
778
|
+
warnings.extend(safety_warnings)
|
|
779
|
+
if effective_restore_database != restore_database:
|
|
780
|
+
warnings.append(
|
|
781
|
+
"restore_database was adjusted to "
|
|
782
|
+
f"{effective_restore_database} to match the target backup "
|
|
783
|
+
"(Home Assistant's Supervisor requires it to match the "
|
|
784
|
+
"backup's database_included flag when restoring Home "
|
|
785
|
+
"Assistant)."
|
|
786
|
+
)
|
|
681
787
|
if safety_backup_id is None:
|
|
682
788
|
warnings.append(
|
|
683
789
|
"No safety backup was created (the default backup "
|
|
@@ -697,7 +803,7 @@ async def restore_backup(
|
|
|
697
803
|
"backup_id": backup_id,
|
|
698
804
|
"status": "Restore initiated - Home Assistant will restart",
|
|
699
805
|
"safety_backup_id": safety_backup_id,
|
|
700
|
-
"restore_database":
|
|
806
|
+
"restore_database": effective_restore_database,
|
|
701
807
|
"warnings": warnings,
|
|
702
808
|
"note": note,
|
|
703
809
|
}
|
|
@@ -761,7 +867,8 @@ def _summarize_backup(entry: dict[str, Any]) -> dict[str, Any]:
|
|
|
761
867
|
"name": entry.get("name"),
|
|
762
868
|
"date": entry.get("date"),
|
|
763
869
|
"size_bytes": size_bytes,
|
|
764
|
-
|
|
870
|
+
# per-agent field, derived from the agents map (see _backup_protected)
|
|
871
|
+
"protected": _backup_protected(entry),
|
|
765
872
|
"database_included": entry.get("database_included"),
|
|
766
873
|
"homeassistant_included": entry.get("homeassistant_included"),
|
|
767
874
|
"homeassistant_version": entry.get("homeassistant_version"),
|
|
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.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/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
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_entities.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_overview.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_scenes.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/smart_search/_scoring.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tool_search_hint_middleware.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
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_dashboard_screenshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/tools/validation_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/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
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.8.1.dev731}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|