ha-mcp-dev 7.8.1.dev730__tar.gz → 7.9.0.dev733__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.
Files changed (144) hide show
  1. {ha_mcp_dev-7.8.1.dev730/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.9.0.dev733}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/backup.py +122 -15
  4. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/LICENSE +0 -0
  6. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/MANIFEST.in +0 -0
  7. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/README.md +0 -0
  8. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/setup.cfg +0 -0
  9. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/__init__.py +0 -0
  10. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/__main__.py +0 -0
  11. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/_pypi_marker +0 -0
  12. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/_version.py +0 -0
  13. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/auth/__init__.py +0 -0
  14. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/auth/consent_form.py +0 -0
  15. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/auth/provider.py +0 -0
  16. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/backup_manager.py +0 -0
  17. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/client/__init__.py +0 -0
  18. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/client/rest_client.py +0 -0
  19. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/client/supervisor_client.py +0 -0
  20. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/client/websocket_client.py +0 -0
  21. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/client/websocket_listener.py +0 -0
  22. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/config.py +0 -0
  23. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  24. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  25. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  26. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/errors.py +0 -0
  27. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/__init__.py +0 -0
  28. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/approval_queue.py +0 -0
  29. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/evaluator.py +0 -0
  30. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/handlers.py +0 -0
  31. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/middleware.py +0 -0
  32. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/model.py +0 -0
  33. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/persistence.py +0 -0
  34. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/policy/value_sources.py +0 -0
  35. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/py.typed +0 -0
  36. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/read_only.py +0 -0
  37. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  38. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  39. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  40. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  41. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  42. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  43. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  44. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  45. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  46. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  47. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  48. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  49. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  50. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  51. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  52. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  53. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  54. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  55. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  56. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  57. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  58. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  59. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/server.py +0 -0
  60. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/settings.css +0 -0
  61. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/settings.js +0 -0
  62. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/settings_ui.py +0 -0
  63. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/smoke_test.py +0 -0
  64. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  65. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/__init__.py +0 -0
  66. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/auto_backup.py +0 -0
  67. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  68. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  69. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/device_control.py +0 -0
  70. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/enhanced.py +0 -0
  71. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/helpers.py +0 -0
  72. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/reference_validator.py +0 -0
  73. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/registry.py +0 -0
  74. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  75. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  76. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  77. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  78. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  79. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  80. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  81. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  82. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  83. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  84. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_addons.py +0 -0
  85. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_areas.py +0 -0
  86. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  87. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  88. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_calendar.py +0 -0
  89. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_camera.py +0 -0
  90. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_categories.py +0 -0
  91. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_code.py +0 -0
  92. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  93. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  94. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  95. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  96. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  97. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  98. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_energy.py +0 -0
  99. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_entities.py +0 -0
  100. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  101. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_groups.py +0 -0
  102. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_hacs.py +0 -0
  103. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_history.py +0 -0
  104. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_integrations.py +0 -0
  105. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_labels.py +0 -0
  106. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  107. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_registry.py +0 -0
  108. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_resources.py +0 -0
  109. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_search.py +0 -0
  110. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_service.py +0 -0
  111. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_services.py +0 -0
  112. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_system.py +0 -0
  113. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_themes.py +0 -0
  114. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_todo.py +0 -0
  115. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_traces.py +0 -0
  116. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_updates.py +0 -0
  117. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_utility.py +0 -0
  118. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  119. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  120. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/tools_zones.py +0 -0
  121. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/util_helpers.py +0 -0
  122. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/tools/validation_middleware.py +0 -0
  123. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/transforms/__init__.py +0 -0
  124. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/transforms/categorized_search.py +0 -0
  125. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  126. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/update_check.py +0 -0
  127. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/__init__.py +0 -0
  128. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/config_hash.py +0 -0
  129. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/data_paths.py +0 -0
  130. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/domain_handlers.py +0 -0
  131. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  132. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  133. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/operation_manager.py +0 -0
  134. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/python_sandbox.py +0 -0
  135. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/skill_loader.py +0 -0
  136. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp/utils/usage_logger.py +0 -0
  137. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  138. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  139. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  140. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  141. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  142. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/tests/__init__.py +0 -0
  143. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/tests/test_constants.py +0 -0
  144. {ha_mcp_dev-7.8.1.dev730 → ha_mcp_dev-7.9.0.dev733}/tests/test_env_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.8.1.dev730
3
+ Version: 7.9.0.dev733
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -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.dev730"
7
+ version = "7.9.0.dev733"
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
- Returns the safety backup ID, or None when password is None (backup intentionally
550
- skipped). Raises ToolError if backup creation fails.
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 created: {safety_backup_id}")
583
- return cast(str, safety_backup_id)
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
- backup_exists = any(b.get("backup_id") == backup_id for b in backups)
684
+ matched = next((b for b in backups if b.get("backup_id") == backup_id), None)
631
685
 
632
- if not backup_exists:
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(ws_client, password, local_agent)
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": 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": 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
- "protected": entry.get("protected"),
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"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.8.1.dev730
3
+ Version: 7.9.0.dev733
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT