ha-mcp-dev 7.8.0.dev710__tar.gz → 7.8.0.dev712__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 (143) hide show
  1. {ha_mcp_dev-7.8.0.dev710/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.8.0.dev712}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/backup_manager.py +108 -9
  4. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/auto_backup.py +14 -11
  5. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_entities.py +1 -0
  6. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_registry.py +3 -0
  7. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  8. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/tests/test_constants.py +1 -1
  9. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/LICENSE +0 -0
  10. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/MANIFEST.in +0 -0
  11. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/README.md +0 -0
  12. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/setup.cfg +0 -0
  13. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/__init__.py +0 -0
  14. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/__main__.py +0 -0
  15. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/_pypi_marker +0 -0
  16. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/_version.py +0 -0
  17. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/auth/__init__.py +0 -0
  18. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/auth/consent_form.py +0 -0
  19. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/auth/provider.py +0 -0
  20. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/client/__init__.py +0 -0
  21. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/client/rest_client.py +0 -0
  22. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/client/supervisor_client.py +0 -0
  23. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/client/websocket_client.py +0 -0
  24. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/client/websocket_listener.py +0 -0
  25. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/config.py +0 -0
  26. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  27. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  28. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  29. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/__init__.py +0 -0
  31. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/approval_queue.py +0 -0
  32. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/evaluator.py +0 -0
  33. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/handlers.py +0 -0
  34. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/middleware.py +0 -0
  35. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/model.py +0 -0
  36. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/persistence.py +0 -0
  37. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/policy/value_sources.py +0 -0
  38. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/py.typed +0 -0
  39. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/read_only.py +0 -0
  40. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  41. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  42. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  43. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  44. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  45. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  46. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  47. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  48. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  49. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  50. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  51. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  52. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  53. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  54. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  55. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  56. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  57. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  58. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  59. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  60. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  61. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  62. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/server.py +0 -0
  63. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/settings.css +0 -0
  64. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/settings.js +0 -0
  65. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/settings_ui.py +0 -0
  66. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/smoke_test.py +0 -0
  67. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  68. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/__init__.py +0 -0
  69. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/backup.py +0 -0
  70. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  71. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  72. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/device_control.py +0 -0
  73. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/enhanced.py +0 -0
  74. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/helpers.py +0 -0
  75. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/reference_validator.py +0 -0
  76. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/registry.py +0 -0
  77. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  78. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  79. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  80. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  81. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  82. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  83. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  84. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  85. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  86. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  87. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_addons.py +0 -0
  88. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_areas.py +0 -0
  89. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  90. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  91. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_calendar.py +0 -0
  92. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_camera.py +0 -0
  93. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_categories.py +0 -0
  94. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_code.py +0 -0
  95. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  96. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  97. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  98. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  99. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  100. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  101. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_energy.py +0 -0
  102. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  103. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_groups.py +0 -0
  104. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_hacs.py +0 -0
  105. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_history.py +0 -0
  106. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_integrations.py +0 -0
  107. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_labels.py +0 -0
  108. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  109. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_resources.py +0 -0
  110. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_search.py +0 -0
  111. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_service.py +0 -0
  112. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_services.py +0 -0
  113. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_system.py +0 -0
  114. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_themes.py +0 -0
  115. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_todo.py +0 -0
  116. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_traces.py +0 -0
  117. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_updates.py +0 -0
  118. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_utility.py +0 -0
  119. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  120. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  121. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/tools_zones.py +0 -0
  122. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/util_helpers.py +0 -0
  123. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/tools/validation_middleware.py +0 -0
  124. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/transforms/__init__.py +0 -0
  125. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/transforms/categorized_search.py +0 -0
  126. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  127. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/__init__.py +0 -0
  128. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/config_hash.py +0 -0
  129. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/data_paths.py +0 -0
  130. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/domain_handlers.py +0 -0
  131. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  132. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  133. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/operation_manager.py +0 -0
  134. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/python_sandbox.py +0 -0
  135. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/skill_loader.py +0 -0
  136. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp/utils/usage_logger.py +0 -0
  137. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  138. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  139. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  140. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  141. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  142. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/tests/__init__.py +0 -0
  143. {ha_mcp_dev-7.8.0.dev710 → ha_mcp_dev-7.8.0.dev712}/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.0.dev710
3
+ Version: 7.8.0.dev712
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.0.dev710"
7
+ version = "7.8.0.dev712"
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"
@@ -991,19 +991,46 @@ async def _fetch_dashboard(client: Any, entity_id: str) -> Any:
991
991
  """
992
992
  from fastmcp.exceptions import ToolError
993
993
 
994
- from .tools.tools_config_dashboards import _get_dashboard_config_internal
994
+ from .tools.tools_config_dashboards import (
995
+ _get_dashboard_config_internal,
996
+ _resolve_dashboard,
997
+ )
998
+
999
+ # The set/delete tools accept BOTH the canonical hyphenated url_path
1000
+ # AND HA's internal (underscored) dashboard id, eagerly resolving the
1001
+ # latter before writing. ``_get_dashboard_config_internal`` does NOT
1002
+ # lazy-resolve, so an internal-id identifier 404s with "Unknown config
1003
+ # specified" and the pre-write snapshot is silently skipped. Pre-resolve
1004
+ # to the canonical url_path so capture works for whichever form the
1005
+ # caller passed (matching the form the write tool ultimately targets).
1006
+ fetch_path = entity_id
1007
+ try:
1008
+ match, _ = await _resolve_dashboard(client, entity_id)
1009
+ if match and match.get("url_path"):
1010
+ fetch_path = match["url_path"]
1011
+ except (HomeAssistantError, ToolError) as err:
1012
+ # Resolver failure (transport/shape) — fall through with the
1013
+ # original identifier; the canonical form is often already correct.
1014
+ logger.debug(
1015
+ "Auto-backup: dashboard resolve failed for %r: %s — using as-is",
1016
+ entity_id,
1017
+ err,
1018
+ )
995
1019
 
996
1020
  try:
997
- config, _config_hash = await _get_dashboard_config_internal(client, entity_id)
1021
+ config, _config_hash = await _get_dashboard_config_internal(client, fetch_path)
998
1022
  except ToolError as err:
999
- # ToolError carries the structured failure payload; treat
1000
- # missing-dashboard responses as "entity doesn't exist yet".
1023
+ # ToolError carries the structured failure payload; treat a
1024
+ # missing/unknown dashboard as "nothing to back up" (also covers a
1025
+ # brand-new dashboard on the create path). "Unknown config
1026
+ # specified" is HA's message for an unresolved url_path.
1001
1027
  msg = str(err).lower()
1002
- if "not_found" in msg or "config_not_found" in msg:
1028
+ if "not_found" in msg or "config_not_found" in msg or "unknown config" in msg:
1003
1029
  return None
1004
1030
  raise
1005
1031
  except HomeAssistantError as err:
1006
- if "not_found" in str(err).lower() or "config_not_found" in str(err).lower():
1032
+ msg = str(err).lower()
1033
+ if "not_found" in msg or "config_not_found" in msg or "unknown config" in msg:
1007
1034
  return None
1008
1035
  raise
1009
1036
  return config
@@ -1305,8 +1332,11 @@ async def _restore_area_or_floor(client: Any, entity_id: str, config: Any) -> An
1305
1332
 
1306
1333
 
1307
1334
  async def _fetch_todo_item(client: Any, entity_id: str) -> Any:
1308
- cal, _, uid = entity_id.partition("::")
1309
- if not cal or not uid:
1335
+ # The second segment is whatever the tool's ``item`` param carried.
1336
+ # ha_set_todo_item / ha_remove_todo_item accept EITHER the item uid OR
1337
+ # its exact summary/name, so this can be either form.
1338
+ cal, _, item_ref = entity_id.partition("::")
1339
+ if not cal or not item_ref:
1310
1340
  return None
1311
1341
  payload = {
1312
1342
  "type": "execute_script",
@@ -1332,7 +1362,11 @@ async def _fetch_todo_item(client: Any, entity_id: str) -> Any:
1332
1362
  result = _require_dict(result, "execute_script")
1333
1363
  items = result.get("response", {}).get("items", {}).get(cal, {}).get("items", [])
1334
1364
  for item in items:
1335
- if item.get("uid") == uid:
1365
+ # Match either form. Matching only on uid silently skipped the
1366
+ # snapshot whenever the caller passed the human-readable summary
1367
+ # (the documented/common case, e.g. ha_remove_todo_item(list, "Buy
1368
+ # milk")) — uid != summary, so the loop found nothing -> None.
1369
+ if item.get("uid") == item_ref or item.get("summary") == item_ref:
1336
1370
  return {"todo_entity_id": cal, **item}
1337
1371
  return None
1338
1372
 
@@ -1366,6 +1400,42 @@ async def _restore_entity_state(client: Any, entity_id: str, config: Any) -> Any
1366
1400
  return await _rest_post(client, f"states/{entity_id}", payload)
1367
1401
 
1368
1402
 
1403
+ # Devices — config/device_registry/{list,update}. ``ha_set_device`` mutates
1404
+ # the user-editable registry fields (name_by_user / area_id / disabled_by /
1405
+ # labels); restore re-applies exactly those. A device deleted by
1406
+ # ``ha_remove_device`` cannot be recreated through the registry, so for that
1407
+ # path the snapshot is an informational pre-delete record and restore is
1408
+ # best-effort.
1409
+
1410
+
1411
+ async def _fetch_device(client: Any, entity_id: str) -> Any:
1412
+ items = await _ws_send(client, {"type": "config/device_registry/list"})
1413
+ if not isinstance(items, list):
1414
+ return None
1415
+ for item in items:
1416
+ if item.get("id") == entity_id:
1417
+ return item
1418
+ return None
1419
+
1420
+
1421
+ async def _restore_device(client: Any, entity_id: str, config: Any) -> Any:
1422
+ # Re-apply the captured registry state. Uses the same field NAMES as
1423
+ # ``_update_device_internal`` but, unlike that partial-update path, always
1424
+ # sends all four — restore reverts the device to the snapshot, so a
1425
+ # captured ``None`` area/name is intentionally re-applied (cleared).
1426
+ return await _ws_send(
1427
+ client,
1428
+ {
1429
+ "type": "config/device_registry/update",
1430
+ "device_id": entity_id,
1431
+ "name_by_user": config.get("name_by_user"),
1432
+ "area_id": config.get("area_id"),
1433
+ "disabled_by": config.get("disabled_by"),
1434
+ "labels": config.get("labels", []),
1435
+ },
1436
+ )
1437
+
1438
+
1369
1439
  # Integration enable/disable — restore re-applies the disabled flag.
1370
1440
 
1371
1441
 
@@ -1435,6 +1505,34 @@ async def _fetch_helper(client: Any, entity_id: str, helper_type: str) -> Any:
1435
1505
  for item in items:
1436
1506
  if item.get("id") == object_id or item.get("id") == entity_id:
1437
1507
  return item
1508
+ # Fallback for renamed helpers: after an entity_id rename the object_id
1509
+ # no longer equals the storage collection id (which stays the original
1510
+ # create-time id == the registry unique_id), so the direct match above
1511
+ # misses and the snapshot was silently skipped. Resolve the unique_id
1512
+ # via the entity registry and match on that — the same key the helper
1513
+ # update tool itself resolves to.
1514
+ eid = entity_id if "." in entity_id else f"{helper_type}.{entity_id}"
1515
+ try:
1516
+ entry = await _ws_send(
1517
+ client, {"type": "config/entity_registry/get", "entity_id": eid}
1518
+ )
1519
+ except HomeAssistantError as err:
1520
+ # Only a genuine "entity not found" means there's nothing to back up;
1521
+ # transport/auth/5xx errors must propagate so maybe_snapshot logs a
1522
+ # WARNING rather than silently skipping. Same POLICY as _fetch_automation,
1523
+ # but matched on the message substring because config/entity_registry/get
1524
+ # failures arrive as a WS command error with no status_code to switch on.
1525
+ # Best-effort: if HA's not-found wording ever changes, a real miss
1526
+ # degrades to a WARNING + skip (never a swallowed fatal error).
1527
+ msg = str(err).lower()
1528
+ if "not_found" in msg or "not found" in msg:
1529
+ return None
1530
+ raise
1531
+ unique_id = entry.get("unique_id") if isinstance(entry, dict) else None
1532
+ if unique_id:
1533
+ for item in items:
1534
+ if str(item.get("id")) == str(unique_id):
1535
+ return item
1438
1536
  return None
1439
1537
 
1440
1538
 
@@ -1505,6 +1603,7 @@ def register_default_handlers(mgr: BackupManager, _client: Any) -> None:
1505
1603
  )
1506
1604
  mgr.register(DomainHandler("todo_item", _fetch_todo_item, _restore_todo_item))
1507
1605
  mgr.register(DomainHandler("entity", _fetch_entity_state, _restore_entity_state))
1606
+ mgr.register(DomainHandler("device", _fetch_device, _restore_device))
1508
1607
  mgr.register(DomainHandler("integration", _fetch_integration, _restore_integration))
1509
1608
  for helper_type in _KNOWN_HELPER_TYPES:
1510
1609
  mgr.register(_make_helper_handler(helper_type))
@@ -75,17 +75,20 @@ def automation_backup_target(kw: dict[str, Any]) -> str:
75
75
  config_id = config.get("id")
76
76
  if config_id:
77
77
  return str(config_id)
78
- identifier = _resolve_str(kw.get("identifier"))
79
- # Strip the leading ``automation.`` prefix when the caller passed an
80
- # entity_id form (typical for ``python_transform`` calls that don't
81
- # carry a config body). Without this, snapshot files duplicate the
82
- # domain segment as ``automation.automation.<slug>.<ts>.yaml`` the
83
- # body's entity_id keeps the prefix, only the filename / list key
84
- # tighten up. HA's automation upsert accepts either form for the
85
- # ``identifier`` param, so restore is unaffected.
86
- if identifier.startswith("automation."):
87
- identifier = identifier[len("automation.") :]
88
- return identifier
78
+ # Return the identifier UNCHANGED — do NOT strip the ``automation.``
79
+ # prefix. Capture and restore resolve the target through
80
+ # ``client.get_automation_config`` -> ``_resolve_automation_id``, which
81
+ # converts an entity_id ("automation.<slug>") to the real numeric
82
+ # ``unique_id`` via a state lookup ONLY when the prefix is present;
83
+ # otherwise it assumes the string already IS a unique_id. Stripping the
84
+ # prefix produced a bare object_id slug that the resolver mis-treats as
85
+ # a unique_id -> GET /config/automation/config/<slug> 404s -> the
86
+ # pre-write snapshot is silently skipped (and, had it resolved, restore
87
+ # would POST to the wrong key and create a stray automation). The
88
+ # doubled domain segment in the snapshot filename
89
+ # ("automation.automation.<slug>.<ts>.yaml") is purely cosmetic and is
90
+ # exactly what the remove path (id_param="identifier") already produces.
91
+ return _resolve_str(kw.get("identifier"))
89
92
 
90
93
 
91
94
  def with_auto_backup(
@@ -1316,6 +1316,7 @@ def register_entity_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
1316
1316
  "title": "Remove Entity",
1317
1317
  },
1318
1318
  )
1319
+ @with_auto_backup(domain="entity", id_param="entity_id", client=client)
1319
1320
  @log_tool_usage
1320
1321
  async def ha_remove_entity(
1321
1322
  entity_id: Annotated[
@@ -14,6 +14,7 @@ from pydantic import Field
14
14
 
15
15
  from ..client.rest_client import HomeAssistantAPIError, HomeAssistantConnectionError
16
16
  from ..errors import ErrorCode, create_error_response
17
+ from .auto_backup import with_auto_backup
17
18
  from .helpers import (
18
19
  exception_to_structured_error,
19
20
  log_tool_usage,
@@ -599,6 +600,7 @@ def register_registry_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
599
600
  tags={"Device Registry"},
600
601
  annotations={"destructiveHint": True, "title": "Set Device"},
601
602
  )
603
+ @with_auto_backup(domain="device", id_param="device_id", client=client)
602
604
  @log_tool_usage
603
605
  async def ha_set_device(
604
606
  device_id: Annotated[
@@ -700,6 +702,7 @@ def register_registry_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
700
702
  "title": "Remove Device",
701
703
  },
702
704
  )
705
+ @with_auto_backup(domain="device", id_param="device_id", client=client)
703
706
  @log_tool_usage
704
707
  async def ha_remove_device(
705
708
  device_id: Annotated[
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.8.0.dev710
3
+ Version: 7.8.0.dev712
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
@@ -20,7 +20,7 @@ NON_ADMIN_TEST_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzNzkyOTc
20
20
  # Home Assistant Docker image for E2E/performance/UAT tests.
21
21
  # Keep in sync with .github/workflows/e2e-tests.yml and pr.yml.
22
22
  # renovate: datasource=docker depName=ghcr.io/home-assistant/home-assistant
23
- HA_TEST_IMAGE = "ghcr.io/home-assistant/home-assistant:2026.6.1"
23
+ HA_TEST_IMAGE = "ghcr.io/home-assistant/home-assistant:2026.6.3"
24
24
 
25
25
  # Test user credentials (for UI access)
26
26
  TEST_USER = "mcp"