ha-mcp-dev 8.3.0.dev2445__tar.gz → 8.3.0.dev2450__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 (290) hide show
  1. {ha_mcp_dev-8.3.0.dev2445/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.3.0.dev2450}/PKG-INFO +1 -1
  2. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/pyproject.toml +1 -1
  3. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +1 -1
  4. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +1 -1
  5. ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/AGENTS.md +40 -0
  6. ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/CLAUDE.md +40 -0
  7. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/README.md +1 -0
  8. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +14 -8
  9. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +30 -0
  10. ha_mcp_dev-8.3.0.dev2450/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/backups.md +128 -0
  11. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +4 -1
  12. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +1 -1
  13. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/server.py +192 -2
  14. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/en.json +2 -1
  15. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.js +126 -36
  16. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/backup.py +18 -3
  17. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_dashboards.py +15 -2
  18. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/skill_loader.py +22 -4
  19. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  20. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/SOURCES.txt +1 -0
  21. ha_mcp_dev-8.3.0.dev2445/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -57
  22. ha_mcp_dev-8.3.0.dev2445/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -57
  23. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/LICENSE +0 -0
  24. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/MANIFEST.in +0 -0
  25. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/README.md +0 -0
  26. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/setup.cfg +0 -0
  27. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/__init__.py +0 -0
  28. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/__main__.py +0 -0
  29. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_pypi_marker +0 -0
  30. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_rich_compat.py +0 -0
  31. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/__init__.py +0 -0
  32. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/requirements.txt +0 -0
  33. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/LICENSE +0 -0
  34. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/MANIFEST.sha256 +0 -0
  35. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/VENDORED +0 -0
  36. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/__init__.py +0 -0
  37. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/__main__.py +0 -0
  38. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/__init__.py +0 -0
  39. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/client.py +0 -0
  40. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/connection.py +0 -0
  41. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/messages.py +0 -0
  42. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/router.py +0 -0
  43. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/asyncio/server.py +0 -0
  44. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/auth.py +0 -0
  45. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/cli.py +0 -0
  46. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/client.py +0 -0
  47. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/connection.py +0 -0
  48. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/datastructures.py +0 -0
  49. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/exceptions.py +0 -0
  50. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/__init__.py +0 -0
  51. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/base.py +0 -0
  52. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/extensions/permessage_deflate.py +0 -0
  53. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/frames.py +0 -0
  54. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/headers.py +0 -0
  55. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/http11.py +0 -0
  56. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/imports.py +0 -0
  57. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/__init__.py +0 -0
  58. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/auth.py +0 -0
  59. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/client.py +0 -0
  60. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/exceptions.py +0 -0
  61. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/framing.py +0 -0
  62. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/handshake.py +0 -0
  63. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/http.py +0 -0
  64. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/protocol.py +0 -0
  65. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/legacy/server.py +0 -0
  66. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/protocol.py +0 -0
  67. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/proxy.py +0 -0
  68. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/py.typed +0 -0
  69. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/server.py +0 -0
  70. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/streams.py +0 -0
  71. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/__init__.py +0 -0
  72. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/client.py +0 -0
  73. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/connection.py +0 -0
  74. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/messages.py +0 -0
  75. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/router.py +0 -0
  76. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/server.py +0 -0
  77. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/sync/utils.py +0 -0
  78. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/__init__.py +0 -0
  79. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/client.py +0 -0
  80. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/connection.py +0 -0
  81. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/messages.py +0 -0
  82. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/router.py +0 -0
  83. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/server.py +0 -0
  84. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/trio/utils.py +0 -0
  85. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/typing.py +0 -0
  86. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/uri.py +0 -0
  87. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/utils.py +0 -0
  88. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_vendor/websockets/version.py +0 -0
  89. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/_version.py +0 -0
  90. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/__init__.py +0 -0
  91. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/consent_form.py +0 -0
  92. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/oidc_compat.py +0 -0
  93. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/auth/provider.py +0 -0
  94. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/backup_manager.py +0 -0
  95. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/browser_landing.py +0 -0
  96. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/__init__.py +0 -0
  97. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/rest_client.py +0 -0
  98. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/supervisor_client.py +0 -0
  99. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/websocket_client.py +0 -0
  100. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/client/websocket_listener.py +0 -0
  101. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/config.py +0 -0
  102. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  103. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  104. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  105. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  106. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  107. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  108. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/errors.py +0 -0
  109. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/hacs_auto_refresh.py +0 -0
  110. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/llm_exposure.py +0 -0
  111. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/log_filters.py +0 -0
  112. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/__init__.py +0 -0
  113. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/approval_queue.py +0 -0
  114. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/editing.py +0 -0
  115. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/evaluator.py +0 -0
  116. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/handlers.py +0 -0
  117. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/middleware.py +0 -0
  118. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/model.py +0 -0
  119. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/persistence.py +0 -0
  120. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/policy/value_sources.py +0 -0
  121. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/py.typed +0 -0
  122. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/read_only.py +0 -0
  123. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/redaction.py +0 -0
  124. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/renamed_tools.py +0 -0
  125. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  126. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  127. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  128. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  129. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  130. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  131. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  132. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  133. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  134. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  135. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  136. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  137. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  138. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  139. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  140. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  141. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/__init__.py +0 -0
  142. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  143. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  144. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  145. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  146. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  147. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  148. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  149. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_locale_policy.py +0 -0
  150. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  151. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  152. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_theme.py +0 -0
  153. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  154. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  155. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/cs.json +0 -0
  156. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/de.json +0 -0
  157. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/eo.json +0 -0
  158. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/es.json +0 -0
  159. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/fr.json +0 -0
  160. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/it.json +0 -0
  161. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/ko.json +0 -0
  162. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/nl.json +0 -0
  163. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/pl.json +0 -0
  164. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
  165. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/sv.json +0 -0
  166. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/tlh.json +0 -0
  167. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
  168. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.css +0 -0
  169. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/settings_ui/settings.html +0 -0
  170. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/smoke_test.py +0 -0
  171. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  172. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/strict_bps.py +0 -0
  173. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/__init__.py +0 -0
  174. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/auto_backup.py +0 -0
  175. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  176. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_result.py +0 -0
  177. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/best_practice_variables.py +0 -0
  178. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/bulk_selector.py +0 -0
  179. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_api.py +0 -0
  180. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_config_entries.py +0 -0
  181. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_config_reads.py +0 -0
  182. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_devices.py +0 -0
  183. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_registries.py +0 -0
  184. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  185. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  186. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_form.py +0 -0
  187. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_menu.py +0 -0
  188. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_flow_walker.py +0 -0
  189. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_identity.py +0 -0
  190. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_reconfigure.py +0 -0
  191. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/config_entry_reload_watch.py +0 -0
  192. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/device_control.py +0 -0
  193. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/error_log_parsing.py +0 -0
  194. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/hacs_registration.py +0 -0
  195. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/helpers.py +0 -0
  196. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/integration_reconfigure.py +0 -0
  197. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_common.py +0 -0
  198. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_sources.py +0 -0
  199. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/log_sources_supervisor.py +0 -0
  200. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/__init__.py +0 -0
  201. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/base.py +0 -0
  202. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/matter.py +0 -0
  203. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/thread.py +0 -0
  204. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  205. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/radio/zwave.py +0 -0
  206. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/reference_validator.py +0 -0
  207. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/registry.py +0 -0
  208. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/renamed_tool_middleware.py +0 -0
  209. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  210. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  211. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  212. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  213. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  214. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  215. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_graph.py +0 -0
  216. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  217. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  218. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  219. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  220. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_addons.py +0 -0
  221. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_areas.py +0 -0
  222. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_assist_pipeline.py +0 -0
  223. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  224. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  225. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_calendar.py +0 -0
  226. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_camera.py +0 -0
  227. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_categories.py +0 -0
  228. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_code.py +0 -0
  229. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  230. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  231. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  232. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  233. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  234. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_dev.py +0 -0
  235. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_energy.py +0 -0
  236. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_entities.py +0 -0
  237. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  238. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_groups.py +0 -0
  239. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_hacs.py +0 -0
  240. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_history.py +0 -0
  241. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_integrations.py +0 -0
  242. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_labels.py +0 -0
  243. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_logs.py +0 -0
  244. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_radio.py +0 -0
  245. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_registry.py +0 -0
  246. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_resources.py +0 -0
  247. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_search.py +0 -0
  248. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_security_policy.py +0 -0
  249. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_service.py +0 -0
  250. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_services.py +0 -0
  251. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_system.py +0 -0
  252. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_themes.py +0 -0
  253. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_todo.py +0 -0
  254. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_traces.py +0 -0
  255. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_updates.py +0 -0
  256. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_utility.py +0 -0
  257. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  258. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  259. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  260. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/tools_zones.py +0 -0
  261. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/util_helpers.py +0 -0
  262. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/tools/validation_middleware.py +0 -0
  263. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/__init__.py +0 -0
  264. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/categorized_search.py +0 -0
  265. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  266. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/transport_security.py +0 -0
  267. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/update_check.py +0 -0
  268. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/__init__.py +0 -0
  269. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/config_hash.py +0 -0
  270. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/config_write_lock.py +0 -0
  271. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/data_paths.py +0 -0
  272. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/domain_handlers.py +0 -0
  273. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/entity_membership.py +0 -0
  274. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  275. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  276. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/operation_manager.py +0 -0
  277. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/python_sandbox.py +0 -0
  278. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/utils/usage_logger.py +0 -0
  279. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/__init__.py +0 -0
  280. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/enforcement.py +0 -0
  281. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/model.py +0 -0
  282. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/persistence.py +0 -0
  283. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp/visibility/resolver.py +0 -0
  284. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  285. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  286. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  287. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  288. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/tests/__init__.py +0 -0
  289. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/tests/test_constants.py +0 -0
  290. {ha_mcp_dev-8.3.0.dev2445 → ha_mcp_dev-8.3.0.dev2450}/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: 8.3.0.dev2445
3
+ Version: 8.3.0.dev2450
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 = "8.3.0.dev2445"
7
+ version = "8.3.0.dev2450"
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
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Agent skills for Home Assistant: best practices for automations, helpers, templates, device control, and dashboards",
9
- "version": "0.15.0"
9
+ "version": "0.18.0"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "home-assistant-skills",
3
3
  "description": "Agent skills for Home Assistant: best practices for automations, helpers, templates, device control, and dashboards.",
4
- "version": "0.15.0",
4
+ "version": "0.18.0",
5
5
  "repository": "https://github.com/homeassistant-ai/skills",
6
6
  "license": "MIT"
7
7
  }
@@ -0,0 +1,40 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Skill Format
6
+
7
+ Every skill is a `SKILL.md` file with YAML frontmatter:
8
+
9
+ ```yaml
10
+ ---
11
+ name: skill-name-here
12
+ description: >
13
+ When to activate this skill and what symptoms it addresses.
14
+ ---
15
+ ```
16
+
17
+ Full authoring constraints: `CONTRIBUTING.md`. The one CI cannot catch:
18
+ - `metadata.version` must be `0` on new skills — do not edit manually; CI assigns the real version on merge
19
+
20
+ ## Skill Authoring Principles
21
+
22
+ - **Context window conservation** — only include domain-specific knowledge that agents lack; omit general programming advice
23
+ - **Conciseness** — provide patterns and quick-reference tables, not tutorials
24
+ - **Consistent terminology** — one term per concept throughout a skill
25
+ - **Symptom-based triggering** — the `description` frontmatter should describe observable agent behaviors that signal the skill is needed
26
+ - **No tool names** — reference HA REST APIs and concepts, never specific MCP tool names (e.g. `ha_rename_entity`); tool names vary by agent setup
27
+
28
+ ## Validation
29
+
30
+ To validate locally:
31
+
32
+ ```bash
33
+ uvx skills-ref validate skills/<skill-name>
34
+ ```
35
+
36
+ ## Reviewing Skill PRs
37
+
38
+ - Judge prose as agent-consumed context, not human docs — the Skill Authoring Principles above are the review bar (e.g. an operator→result lookup table beats narrative bullets, because agents land here holding one case to resolve)
39
+ - Skills make version-pinned claims about HA behavior — verify against source at the release tag: `gh api repos/home-assistant/core/contents/<path>?ref=2026.7.4 --jq .content | base64 -d`
40
+ - Community PRs come from forks: base-repo `?ref=<pr-branch>` 404s; get the fork with `gh pr view <pr> --json headRepository` and fetch files from there
@@ -0,0 +1,40 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Skill Format
6
+
7
+ Every skill is a `SKILL.md` file with YAML frontmatter:
8
+
9
+ ```yaml
10
+ ---
11
+ name: skill-name-here
12
+ description: >
13
+ When to activate this skill and what symptoms it addresses.
14
+ ---
15
+ ```
16
+
17
+ Full authoring constraints: `CONTRIBUTING.md`. The one CI cannot catch:
18
+ - `metadata.version` must be `0` on new skills — do not edit manually; CI assigns the real version on merge
19
+
20
+ ## Skill Authoring Principles
21
+
22
+ - **Context window conservation** — only include domain-specific knowledge that agents lack; omit general programming advice
23
+ - **Conciseness** — provide patterns and quick-reference tables, not tutorials
24
+ - **Consistent terminology** — one term per concept throughout a skill
25
+ - **Symptom-based triggering** — the `description` frontmatter should describe observable agent behaviors that signal the skill is needed
26
+ - **No tool names** — reference HA REST APIs and concepts, never specific MCP tool names (e.g. `ha_rename_entity`); tool names vary by agent setup
27
+
28
+ ## Validation
29
+
30
+ To validate locally:
31
+
32
+ ```bash
33
+ uvx skills-ref validate skills/<skill-name>
34
+ ```
35
+
36
+ ## Reviewing Skill PRs
37
+
38
+ - Judge prose as agent-consumed context, not human docs — the Skill Authoring Principles above are the review bar (e.g. an operator→result lookup table beats narrative bullets, because agents land here holding one case to resolve)
39
+ - Skills make version-pinned claims about HA behavior — verify against source at the release tag: `gh api repos/home-assistant/core/contents/<path>?ref=2026.7.4 --jq .content | base64 -d`
40
+ - Community PRs come from forks: base-repo `?ref=<pr-branch>` 404s; get the fork with `gh pr view <pr> --json headRepository` and fetch files from there
@@ -76,6 +76,7 @@ The `home-assistant-best-practices` skill includes:
76
76
  | `references/examples.yaml` | Compound examples combining multiple best practices |
77
77
  | `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact |
78
78
  | `references/blueprint-guide.md` | Blueprint authoring: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, `!input` templating, versioning |
79
+ | `references/backups.md` | Full instance backups vs rolling one object back, when an operation needs a backup first, what an archive contains, encryption keys, restore verification, backup deletion |
79
80
 
80
81
  ## Contributing
81
82
 
@@ -5,14 +5,14 @@ description: >
5
5
 
6
6
  TRIGGER THIS SKILL WHEN:
7
7
  - Creating or editing automations, scripts, scenes, or dashboards
8
- - Choosing between template sensors and built-in helpers
9
- - Restructuring triggers, conditions, or automation modes
10
- - Setting up Zigbee button/remote automations
8
+ - Choosing template sensors vs built-in helpers
9
+ - Restructuring triggers, conditions, or modes
10
+ - Zigbee button/remote automations
11
11
  - Renaming entities or migrating device_id to entity_id
12
- - Configuring dashboard cards or selecting helpers
13
12
  - Looking up card types or domain docs
14
- - Writing or reviewing AppDaemon apps
15
- - Authoring or editing reusable Blueprints
13
+ - AppDaemon apps
14
+ - Authoring reusable Blueprints
15
+ - About to delete a backup, restore a backup, or upgrade Core or the OS
16
16
 
17
17
  SYMPTOMS:
18
18
  - Agent uses Jinja2 templates where native options exist
@@ -22,9 +22,10 @@ description: >
22
22
  - Agent hard-codes values or uses raw sensor over helper
23
23
  - Agent edits .storage, writes YAML, or generates YAML snippets
24
24
  - Agent tells user to edit configuration.yaml for UI integrations
25
- - Agent hardcodes entities in a Blueprint or uses free-text input over a selector
25
+ - Agent hardcodes Blueprint entities or uses free-text over a selector
26
+ - Agent changes existing state with no recovery path
26
27
  metadata:
27
- version: 15
28
+ version: 18
28
29
  ---
29
30
 
30
31
  # Home Assistant Best Practices
@@ -122,6 +123,10 @@ See `references/device-control.md#zigbee-buttonremote-patterns`.
122
123
  | Free-text input for an entity/device in a Blueprint | Typed `entity`/`target`/`device` selector | Text lets typos through and fails silently; selectors validate the choice | `references/blueprint-guide.md#inputs-and-selectors` |
123
124
  | `!input` used directly inside a template | Bind it to a `variables:` entry, use the variable | `!input` is a YAML tag, not a template value — the template errors or ignores it | `references/blueprint-guide.md#referencing-inputs-input-and-templating` |
124
125
  | Publishing a Blueprint without `source_url` | Set `source_url` to the file's canonical URL | Without it users can't re-import updates and sharing is awkward | `references/blueprint-guide.md#blueprint-metadata` |
126
+ | Full instance restore to undo one automation/script/scene/dashboard/helper edit | Roll that object back — write its previous definition back through the config API | A full restore reverts every unrelated change made since the backup and restarts HA | `references/backups.md#which-recovery-path` |
127
+ | Deleting a device/entity/integration, or upgrading Core, without a preceding backup | Take the full backup *before* the operation | Registry deletions can't be undone by writing the old value back, and a backup taken afterward captures the damage | `references/backups.md#when-a-full-backup-earns-its-cost` |
128
+ | Restoring a backup, deleting a backup, or upgrading Core or the OS without explicit user confirmation | Ask, name the concrete effect, and wait for an answer — every time, backup or not | A full restore discards everything since the archive for all restored parts and restarts HA; a Supervisor partial restore overwrites only the selected archive parts; deletion destroys a recovery point; a Core/OS upgrade is high-impact and its recovery path IS the pre-upgrade backup | `references/backups.md` |
129
+ | Calling a service "reversible" without naming its inverse | Treat an operation as reversible only when you can name and target its exact inverse | `vacuum.send_command`, `*.press` and `notify.*` have none; and an exact inverse still doesn't undo the consequence — re-locking a door doesn't un-expose the house | `references/backups.md#when-a-full-backup-earns-its-cost` |
125
130
 
126
131
  ---
127
132
 
@@ -144,3 +149,4 @@ Read these when you need detailed information:
144
149
  | `references/examples.yaml` | Need compound examples combining multiple best practices | — |
145
150
  | `references/appdaemon.md` | AppDaemon apps: when to use vs. native HA, app structure, service calls, scheduling, error handling, safe refactoring impact | — |
146
151
  | `references/blueprint-guide.md` | Authoring reusable blueprints: metadata & `source_url`, inputs & selectors, `target` vs `entity`, defaults, input sections, `!input` templating, versioning | `#when-to-author-a-blueprint`, `#blueprint-metadata`, `#inputs-and-selectors`, `#target-selector-vs-entity-selector`, `#defaults`, `#input-sections`, `#referencing-inputs-input-and-templating`, `#versioning-and-updates`, `#common-pitfalls` |
152
+ | `references/backups.md` | Deciding whether an operation needs a backup first; choosing between a full restore, a partial restore, and rolling one object back; what an archive actually contains; encryption keys and the emergency kit; restore verification; what HA does and does not protect when deleting a backup | `#which-recovery-path`, `#when-a-full-backup-earns-its-cost`, `#what-a-full-backup-contains`, `#restoring-consequences-and-verification`, `#deleting-backups`, `#common-pitfalls` |
@@ -927,6 +927,36 @@ variables:
927
927
 
928
928
  `trigger_variables:` is a separate top-level key evaluated **before** triggers fire — it supports **limited templates only** (no `states()`/`state_attr()`), mainly for passing a blueprint `!input` into trigger options. Don't put state-based templates there.
929
929
 
930
+ ### Keys render in order, one at a time
931
+
932
+ A `variables:` block renders one key at a time, each rendered result feeding the context for the next. A key that reads a name declared **further down the same block** reads it while it is still undefined.
933
+
934
+ ```yaml
935
+ # AVOID — `total` is still undefined when `msg` renders
936
+ variables:
937
+ msg: "Total: {{ total }}"
938
+ total: "{{ states('sensor.a') | float(0) + states('sensor.b') | float(0) }}"
939
+
940
+ # CORRECT — declare before use
941
+ variables:
942
+ total: "{{ states('sensor.a') | float(0) + states('sensor.b') | float(0) }}"
943
+ msg: "Total: {{ total }}"
944
+ ```
945
+
946
+ What the forward read costs depends on how the template uses the name (behavior as of 2026.7):
947
+
948
+ | Use of the undefined name | Result |
949
+ |---|---|
950
+ | `x.attr`, `x['k']`, arithmetic, `<`/`>`/`<=`/`>=`, `\| int`, `\| float` | Raises `UndefinedError`, logs ERROR — **aborts the run** |
951
+ | `{{ x }}`, `{% if x %}`, `{% for i in x %}`, `'p' ~ x` | Empty and falsy, logs WARNING — runs on into the else branch |
952
+ | `x \| length`, `x == y`, `x != y` | `0` / `False` / `True` — **no log and no trace entry** |
953
+
954
+ For the aborting row, [`continue_on_error:`](#continue-on-error) is no escape — it is an action option, a top-level block is not an action, and on a mid-sequence step it cannot suppress a render error. A default like `| int(0)` does not save it either: the default covers an unconvertible value, not an undefined name. In the silent row, watch `!=`: `{% if later != 'x' %}` on a name that is not defined yet takes the **then** branch, leaving nothing behind to explain why.
955
+
956
+ Not a problem when the name also exists in an enclosing scope — the reference then resolves outward to that value. The two placements diverge on that collision, though: a top-level `variables:` block **skips** a key already present in the incoming scope, so the incoming value stands and the block's own definition never takes effect for that run, while a mid-sequence `- variables:` step renders and **overwrites** it. Staging a computation across consecutive `variables:` steps is therefore mid-sequence only.
957
+
958
+ When an automation declares both `trigger_variables:` and `variables:`, the two render as one sequential mapping with `trigger_variables:` first, so a `variables:` key may read a `trigger_variables:` key but not the reverse. That holds for names declared in only one of the two: a name in both merges into a single entry that keeps the `trigger_variables:` position but takes the `variables:` template, so it renders early — before any `trigger_variables:` key declared after it. The separate attach-time render noted above happens before any `variables:` key exists.
959
+
930
960
  ---
931
961
 
932
962
  ## Capturing Action Responses
@@ -0,0 +1,128 @@
1
+ # Backups and Recovery
2
+
3
+ **Three different things get called a "backup" in Home Assistant work.** This file covers the first two, and only the first is a Home Assistant feature:
4
+
5
+ 1. A **full instance backup** — the archive Home Assistant creates at *Settings → System → Backups*, which is also the surface for restoring and deleting one.
6
+ 2. **Object rollback** — not an HA feature but a workflow: fetch an object's current definition *before* editing it, then write that definition back through the same config API to undo. One object, no restart.
7
+ 3. The **pre-edit file copy** a managed YAML write takes — a different mechanism entirely, covered in `references/yaml-only-integrations.md`.
8
+
9
+ **Three operations need explicit user confirmation before you act — every time, including when a backup already exists.** They differ in kind, so say which one applies:
10
+
11
+ - **Restoring a backup** — *irreversible*: it discards every change made since that archive and restarts HA. The only way back is another backup taken before the restore.
12
+ - **Deleting a backup** — *irreversible*: the archive is gone, and with it any recovery point it was the last copy of.
13
+ - **Upgrading Core or the OS** — *high-impact rather than strictly irreversible*: it can break integrations and custom components, and the recovery path is restoring the pre-upgrade backup, which is why one has to exist first.
14
+
15
+ A backup lowers recovery risk; it does not authorize the action.
16
+
17
+ ## Table of Contents
18
+ 1. [Which Recovery Path](#which-recovery-path)
19
+ 2. [When a Full Backup Earns Its Cost](#when-a-full-backup-earns-its-cost)
20
+ 3. [What a Full Backup Contains](#what-a-full-backup-contains)
21
+ 4. [Restoring: Consequences and Verification](#restoring-consequences-and-verification)
22
+ 5. [Deleting Backups](#deleting-backups)
23
+ 6. [Common Pitfalls](#common-pitfalls)
24
+
25
+ ---
26
+
27
+ ## Which Recovery Path
28
+
29
+ | Situation | What to do |
30
+ |-----------|------------|
31
+ | One automation / script / scene / dashboard / helper edit went wrong | **Roll the object back** — write its previous definition back through the config API. No restart, nothing else touched. Fetching before writing is what makes this possible; see `references/safe-refactoring.md#universal-workflow`. |
32
+ | Several objects were edited in one session | **Roll each object back**, one at a time. Still no restart. |
33
+ | A device, entity, or area was deleted from the registry | **Restore a full backup.** Registry state — IDs, area/label assignments, and the references other config held to them — cannot be recovered by writing a definition back. |
34
+ | An integration or config entry was removed | **Restore a full backup.** Its entities went with it. |
35
+ | An App update or removal broke something | **Restore a full backup partially** — that App and its data only. Supervisor installs (OS/Supervised) only; elsewhere it is a full restore. |
36
+ | A Core or Operating System upgrade broke the instance | **Restore a full backup.** |
37
+ | The instance no longer boots | **Restore a full backup** through the Supervisor / onboarding restore flow. |
38
+
39
+ Prefer the narrowest path that can fix the fault. Object rollback is scoped to one object, needs no restart, and cannot lose unrelated work.
40
+
41
+ **Partial restore is a narrower restore, not a rollback.** HA's partial restore (`hassio.restore_partial`) picks which parts of an existing backup to put back — Home Assistant settings, specific Apps, and folders. Including Home Assistant settings restarts HA. That is a different mechanism from writing one object's definition back through the config API, which restarts nothing.
42
+
43
+ **Partial restore needs a Supervisor**, so it exists on Home Assistant OS and Supervised only. On Container and Core installs the `hassio` integration is not loaded, there is no partial restore, and the recovery options are object rollback or a full restore through whatever backup mechanism that install uses. Check which install you are on before offering a partial restore as a step.
44
+
45
+ ## When a Full Backup Earns Its Cost
46
+
47
+ The test is reversibility, not how risky something feels: **back up before an operation you cannot undo by writing the old value back.**
48
+
49
+ **Irreversible — take the backup first, and confirm the operation with the user:**
50
+ - Deleting a device, entity, or area from the registry.
51
+ - Removing an integration or config entry.
52
+ - Removing an App, or a major App version jump.
53
+ - Core / Operating System upgrades.
54
+ - Restoring a *different* backup — a restore is itself destructive of current state.
55
+ - Bulk operations across many objects whose before-state was not captured.
56
+ - Editing a Config-Entry integration whose fields have no post-setup API write path — see `references/safe-refactoring.md#config-entry-data--blind-spots-for-entity-registry-renames`.
57
+
58
+ **Reversible — a backup is usually redundant:**
59
+ - Editing an automation / script / scene / dashboard / helper whose current definition was fetched first. Writing that definition back *is* the undo.
60
+ - Renaming a friendly name, changing an icon, moving an entity between areas.
61
+ - A service call whose inverse you can name and target identically — `light.turn_on` ↔ `light.turn_off` on the same entity.
62
+
63
+ **Neither list — treat as irreversible until you have checked.** The exact-inverse test is the rule; these are the cases where passing it still is not enough:
64
+
65
+ - **No inverse exists.** `vacuum.send_command`, any `*.press`, `notify.*` — nothing to call to undo them.
66
+ - **The inverse exists but does not undo the consequence.** `lock.unlock` ↔ `lock.lock` and `cover.open_cover` ↔ `cover.close_cover` are exact inverses, yet the door was unlocked and the cover was open in between. Re-locking restores the state, not the exposure. Confirm these with the user first even though they are mechanically reversible.
67
+ - **The effect has already left Home Assistant.** A sent notification or a fired webhook cannot be recalled, whatever you call next.
68
+
69
+ "It's just a service call" is not a reversibility argument — the inverse depends on the service and the target, and a reversible state change is not always a reversible event.
70
+
71
+ Two things this test depends on:
72
+
73
+ - **Timing is the whole point.** A backup taken *after* the destructive step captures the damage. It must precede the operation.
74
+ - **An existing schedule does not substitute.** On an instance with nightly automatic backups, the newest one can be almost a day stale — fine for reversible edits, not for an irreversible operation about to run now.
75
+
76
+ ## What a Full Backup Contains
77
+
78
+ Backup contents are **selected per backup**, so what a given archive holds is a property of that archive, not of Home Assistant:
79
+
80
+ - **Home Assistant settings** — the config directory: `.storage` registries, YAML configuration, and the stored automations, scripts, scenes, dashboards, and helpers.
81
+ - **The recorder database — usually included, but confirm rather than assume.** Home Assistant Core's own backup config defaults `include_database` to true (2026.8.1), so history and long-term statistics normally *are* in the archive and *do* come back. Three things change that answer: the option can be switched off for an individual backup; on Supervised/OS the Supervisor API takes `homeassistant_exclude_database` per backup and the `backups_exclude_database` setting can make exclusion the *default* for every future one; and a recorder pointed at an external database lives outside the archive entirely. Check the archive and the install before telling a user their history will or won't return.
82
+ - **Apps**, individually selectable, with their data directories.
83
+ - **Folders**, individually selectable: `share`, `addons/local`, `ssl`, `media`.
84
+
85
+ Nothing else on the host is included, and a restore overwrites only the parts a given backup actually contains.
86
+
87
+ **Encryption and secrets.** Backups are password-protected by default, and restoring one requires its encryption key — the key issued in the backup emergency kit when HA sets up encrypted backups. **An archive without its key is not a recovery point**, so verify the user has the emergency kit before treating a backup as a fallback. A backup downloaded through the UI is decrypted on the way out, and that copy carries `.storage` in cleartext — credentials, long-lived tokens, API keys. Protect the archive and the emergency kit as separate secrets, and never move either anywhere the user has not chosen.
88
+
89
+ ## Restoring: Consequences and Verification
90
+
91
+ - **Confirm before restoring.** Naming which changes will be lost is part of the ask.
92
+ - A **full restore** reverts every included part to its state at backup time and restarts HA. Unrelated work done in that window is gone.
93
+ - A **partial restore** narrows the blast radius; prefer it whenever the fault is localised. Including Home Assistant settings still restarts HA.
94
+ - An **object rollback** through the config API restarts nothing and touches one object.
95
+
96
+ After any restore, verify rather than assume:
97
+ - Entities are available, not `unavailable` — a restored config entry whose credentials expired comes back broken.
98
+ - The error log is clean of startup failures.
99
+ - Cloud-backed integrations may need re-authentication; a restore replays a token that may have been rotated since.
100
+ - Objects that were disabled when the backup was taken come back disabled.
101
+
102
+ ## Deleting Backups
103
+
104
+ Be precise about what Home Assistant protects here, because it is less than it looks:
105
+
106
+ - The **retention cleanup** that enforces a backup schedule will not delete the last remaining backup for a backup location.
107
+ - A **direct delete of one specific backup** — the operation performed when a user asks for a particular backup to be removed — has **no such guard**, and there is no minimum-age protection anywhere. Delete the only recovery point and Home Assistant will carry it out.
108
+
109
+ So on this operation the caution has to come from whoever is driving, not from the platform:
110
+
111
+ - **Confirm the specific backup with the user before deleting it** — name which one and how old it is.
112
+ - **Leave at least one recent, usable recovery point.** Usable includes having its encryption key available.
113
+ - **Delete oldest first** when clearing several.
114
+
115
+ Storage pressure is not the only legitimate reason to delete. A credential compromise can make the archive itself the liability; a privacy or retention requirement can mandate removal; and a user can simply ask. None of those removes the confirmation step or the keep-one-recovery-point guidance.
116
+
117
+ ## Common Pitfalls
118
+
119
+ - **Backing up after the destructive step.** The archive now contains the damage.
120
+ - **Using a full restore to undo one config edit.** It reverts every unrelated change since, and restarts HA.
121
+ - **Assuming a direct backup delete is guarded.** It isn't — only the scheduled retention cleanup keeps a last backup.
122
+ - **Treating an archive without its encryption key as a recovery point.** It cannot be restored.
123
+ - **Treating a UI-downloaded backup as an ordinary file.** It is decrypted, and it holds cleartext credentials and tokens.
124
+ - **Claiming history will or won't come back without checking.** The recorder database is included by default, but per-backup selection and external databases both change the answer.
125
+ - **Calling a service "reversible" without naming its inverse** — or treating a mechanical inverse as undoing the consequence, when the door was still unlocked in between.
126
+ - **Treating a backup as a substitute for fetching the current definition before an edit.** Fetching is cheaper, object-scoped, and needs no restart to undo.
127
+ - **Keeping backups only on the instance they back up.** One disk or host failure takes the instance and its recovery points together.
128
+ - **Acting on an irreversible operation because a backup exists.** The backup is the safety net, not the authorization — the user's confirmation is.
@@ -22,7 +22,7 @@ This document covers Home Assistant's built-in helpers and integrations that sho
22
22
  Helpers reach Home Assistant through two different creation mechanisms — which one a helper uses determines whether you submit flat fields in a single step or step through a config flow:
23
23
 
24
24
  - **Storage-collection helpers** — created via a per-domain WebSocket collection command (`<domain>/create`, e.g. `input_boolean/create`, `counter/create`) with flat, structured fields: `input_boolean`, `input_number`, `input_select`, `input_text`, `input_datetime`, `input_button`, `counter`, `timer`, `schedule`, `zone`, `person`, `tag`. (`schedule` additionally offers an optional YAML mode.)
25
- - **Config-entry-flow helpers** — created through the generic config-entry flow (`config_entries/flow`, handler = the helper domain), often a multi-step flow that begins with a sub-type menu (see [Menu-Based Helpers](#menu-based-helpers)): `template`, `group`, `utility_meter`, `derivative`, `min_max`, `threshold`, `integration`, `statistics`, `trend`, `random`, `filter`, `tod`, `generic_thermostat`, `generic_hygrostat`, `switch_as_x`, `bayesian`, `mold_indicator`.
25
+ - **Config-entry-flow helpers** — created through the generic config-entry flow (`config_entries/flow`, handler = the helper domain), often a multi-step flow that begins with a sub-type menu (see [Menu-Based Helpers](#menu-based-helpers)): `template`, `group`, `utility_meter`, `derivative`, `min_max`, `threshold`, `integration`, `statistics`, `trend`, `random`, `filter`, `tod`, `generic_thermostat`, `generic_hygrostat`, `switch_as_x`, `bayesian`, `mold_indicator`, `history_stats`.
26
26
 
27
27
  ## Menu-Based Helpers
28
28
 
@@ -357,6 +357,9 @@ sensor:
357
357
  **Key behaviors:**
358
358
  - Limited by recorder's `purge_keep_days`
359
359
  - Updates when source changes or once per minute
360
+ - Creatable via the config-entry flow (multi-step: entity and type, then
361
+ tracked states, then the period — exactly two of `start`/`end`/`duration`);
362
+ YAML remains supported but is not required
360
363
 
361
364
  **Common uses:**
362
365
  - How long lights were on today
@@ -205,7 +205,7 @@ Iterate the returned entries and check `data` and `options` fields for the old e
205
205
 
206
206
  For integrations that store entity_ids in `options` (Generic Thermostat, Generic Hygrostat, Threshold Helper, Min/Max Helper): use the Options Flow. See the Config-Entry-Groups section above for the full Options Flow pattern.
207
207
 
208
- For integrations that store entity_ids in `data` (Better Thermostat): `data` fields written during the initial Config Flow setup have no standard API for post-setup mutation — the Options Flow updates `options` only. No API-based fix path exists. Document this limitation to the user before proceeding with a rename.
208
+ For integrations that store entity_ids in `data` (Better Thermostat): `data` fields written during the initial Config Flow setup have no standard API for post-setup mutation — the Options Flow updates `options` only. No API-based fix path exists. Document this limitation to the user before proceeding with a rename. This is the one operation in this guide that a fetch-before-write rollback cannot undo, so treat the rename as irreversible and take a full backup first — see `references/backups.md#when-a-full-backup-earns-its-cost`.
209
209
 
210
210
  ---
211
211
 
@@ -828,10 +828,152 @@ class HomeAssistantSmartMCPServer:
828
828
  'mean "search failed", not "no results" — see `partial_reason` '
829
829
  "(also mirrored into `warnings`). Do not treat a partial response as "
830
830
  "complete.\n\n"
831
- "For parameters, schema, and examples, see ha_get_skill_guide."
831
+ "For what to do with what you find — native-first patterns, "
832
+ "entity_id over device_id, impact analysis before renaming — see "
833
+ "ha_get_skill_guide."
834
+ ),
835
+ # ha_manage_backup: the full description is 4571 dedented chars at
836
+ # BACKUP_HINT=normal (4672 strong / 4598 weak) and the lite value is
837
+ # a little over a quarter of that. No exact pair is quoted here on
838
+ # purpose — three successive edits left a stale figure in this
839
+ # comment, so the ratio is pinned by
840
+ # test_backup_lite_description_stays_substantially_smaller instead,
841
+ # which measures both sides at test time.
842
+ #
843
+ # It was the largest full description in the GATEWAY catalog this was
844
+ # measured against, not in the repo: ha_eval_template (5834) and
845
+ # ha_get_system_health (5811) are both larger and both unmapped.
846
+ # The reduction is smaller than pure compression would give because
847
+ # the safety content below is kept inline. The routing matrix STAYS —
848
+ # the `action` parameter's own Field description says "Valid (scope,
849
+ # action) combinations are listed in the tool description", so
850
+ # trimming it away would leave that pointer aimed at nothing.
851
+ #
852
+ # The deferral target now exists: homeassistant-ai/skills#76 landed
853
+ # references/backups.md, and the submodule pin in this commit
854
+ # includes it, so test_every_lite_destination_resolves enforces it
855
+ # rather than the entry sitting on "self-contained". What defers is
856
+ # the recovery-layer judgment — which of HA's two paths fits which
857
+ # failure, what an archive actually contains, encryption keys, and
858
+ # what HA does and does not protect on delete. The eleven worked
859
+ # examples stay dropped: they were call syntax, which the input
860
+ # schema already carries.
861
+ #
862
+ # destructiveHint is set on this tool, so the lite text keeps every
863
+ # irreversibility marker inline rather than deferring it: the
864
+ # restart, the confirm, the human-only enable_snapshot_delete, and
865
+ # each individual delete guard. It also keeps the two diagnostics
866
+ # that have no fallback anywhere else — the enable_auto_backup
867
+ # empty-list ambiguity, and the {backup_hint_text} timing sentence
868
+ # (resolved per-instance; see _lite_docstring_tokens).
869
+ "ha_manage_backup": (
870
+ "Manage Home Assistant backups: full HA snapshots "
871
+ "(`scope='snapshot'`) and per-entity auto-backups of agent edits "
872
+ "(`scope='edits'`). Pick the scope first — the wrong one routes "
873
+ "through the wrong code path.\n\n"
874
+ "`scope='snapshot'` actions: `create` (slow on a large "
875
+ "instance — progress heartbeats are sent while waiting, so a "
876
+ "long wait is not a hang; retrying starts a SECOND backup), "
877
+ "`list`, `restore` (**restarts HA**), `delete` (needs "
878
+ "`confirm=True`; disabled until a human sets "
879
+ "`enable_snapshot_delete` — an agent cannot enable it. Even then "
880
+ "a delete is refused for scheduled/automatic backups, for "
881
+ "anything younger than `snapshot_delete_min_age_days` (default "
882
+ "7; 0 disables the floor), and for the single newest snapshot "
883
+ "remaining).\n\n"
884
+ "`scope='edits'` actions: `create` (needs `domain` + "
885
+ "`entity_id`), `list`, `view`, `diff`, `restore` (takes a fresh "
886
+ "safety snapshot first; no HA restart), `delete`. Automatic "
887
+ "capture on write is gated by `enable_auto_backup`, so an empty "
888
+ '`list` means "nothing saved" OR "the toggle is off" — check it '
889
+ "before concluding there is nothing to restore. Either way "
890
+ "`(edits, create)` still works: it bypasses the toggle because "
891
+ "the request is explicit.\n\n"
892
+ "Use `edits` to undo a recent agent edit to an "
893
+ "automation/script/scene/dashboard/helper; use `snapshot` for "
894
+ "system-wide recovery and before irreversible operations. "
895
+ "{backup_hint_text}\n\n"
896
+ "For which recovery path fits which failure, what an archive "
897
+ "actually contains, and the encryption key a restore needs, see "
898
+ "ha_get_skill_guide (`references/backups.md`)."
899
+ ),
900
+ # ha_report_issue: the dedented docstring is what gets advertised,
901
+ # not the raw indented one (2351 chars), and the lite value is about
902
+ # a third of it. Same reason as above for not quoting a pair.
903
+ #
904
+ # Unlike every other entry here, the deferral target is the tool's
905
+ # OWN RESPONSE, not the skill guide: `instructions` (see
906
+ # tools_bug_report.py) independently re-derives the duplicate check,
907
+ # the template choice, the missing-tool pre-check, and the mandatory
908
+ # anonymisation step. Issue reporting is ha-mcp product meta — it
909
+ # cannot go in the skill pack, whose CONTRIBUTING forbids coupling
910
+ # skill content to specific MCP tool names. The lite text says so
911
+ # outright so a compliant agent doesn't spend a call finding out.
912
+ "ha_report_issue": (
913
+ "Get diagnostic information plus a ready-to-file report "
914
+ "template. Covers two kinds of report: a runtime bug (ha-mcp "
915
+ "errored or behaved unexpectedly) and agent-behaviour feedback "
916
+ "(the AI used the wrong tool or worked inefficiently). Pick "
917
+ "based on whether the fault was in ha-mcp or in the agent's own "
918
+ "choices.\n\n"
919
+ "The response carries the full workflow in its `instructions` "
920
+ "field — duplicate check, template selection, the mandatory "
921
+ "anonymisation step, and the submit URLs — plus "
922
+ '`missing_tool_hint` for the "a tool I expected is missing" '
923
+ "case, which is usually a stale client tool list rather than a "
924
+ "bug. Read `instructions` before presenting anything to the "
925
+ "user; ha_get_skill_guide does not cover issue reporting."
832
926
  ),
833
927
  }
834
928
 
929
+ # Where each _LITE_DOCSTRINGS entry's "see ..." pointer actually lands.
930
+ #
931
+ # The pointer is the whole bargain of lite mode: the trimmed text is only
932
+ # acceptable because the detail is reachable. Enforcing that every entry
933
+ # merely CONTAINS the string "ha_get_skill_guide" (the original
934
+ # invariant) checks the pointer and never the destination — which is how
935
+ # an entry deferring to guide content that does not exist could pass
936
+ # tests (#2153 review). This map names the destination so
937
+ # tests/src/unit/test_lite_docstrings.py can resolve it against the
938
+ # vendored skill pack.
939
+ #
940
+ # Three legal value forms, each with a matching check in the tests:
941
+ #
942
+ # "references/<file>.md" / "SKILL.md"
943
+ # A path inside the home-assistant-best-practices skill. Must
944
+ # resolve against the VENDORED pack, and the lite text must carry
945
+ # a ha_get_skill_guide pointer to reach it.
946
+ # "tool-response:<field>"
947
+ # The guidance ships in the tool's own response instead. The field
948
+ # must actually be returned, and the lite text must name it.
949
+ # "self-contained"
950
+ # The entry defers nothing. The lite text must carry NO
951
+ # ha_get_skill_guide pointer and name no reference file, so an
952
+ # entry cannot quietly re-acquire a pointer to content that isn't
953
+ # vendored — which is what "self-contained" exists to prevent.
954
+ _LITE_DOCSTRING_DESTINATIONS: ClassVar[dict[str, str]] = {
955
+ "ha_config_get_automation": "references/automation-patterns.md",
956
+ "ha_config_set_automation": "references/automation-patterns.md",
957
+ "ha_config_get_script": "references/automation-patterns.md",
958
+ "ha_config_set_script": "references/automation-patterns.md",
959
+ "ha_config_get_scene": "references/scenes.md",
960
+ "ha_config_set_scene": "references/scenes.md",
961
+ "ha_config_list_helpers": "references/helper-selection.md",
962
+ "ha_config_set_helper": "references/helper-selection.md",
963
+ "ha_config_get_dashboard": "references/dashboard-cards.md",
964
+ "ha_config_set_dashboard": "references/dashboard-guide.md",
965
+ "ha_call_service": "references/domain-docs.md",
966
+ "ha_config_set_yaml": "references/yaml-only-integrations.md",
967
+ # The skill pack has no search reference, so this lands on the
968
+ # decision workflow. The lite text was reworded to promise what
969
+ # SKILL.md actually holds (what to do with the results) instead of
970
+ # "parameters, schema, and examples", which it never had — the
971
+ # parameters ship in the input schema regardless.
972
+ "ha_search": "SKILL.md",
973
+ "ha_manage_backup": "references/backups.md",
974
+ "ha_report_issue": "tool-response:instructions",
975
+ }
976
+
835
977
  # Description overrides that REPLACE the original description for BM25.
836
978
  # Used to narrow overly broad tools so they stop matching generic queries
837
979
  # against ha-mcp's internal BM25 search tool. Only applied when
@@ -840,6 +982,50 @@ class HomeAssistantSmartMCPServer:
840
982
  # unnecessarily trim context for other clients.
841
983
  _SEARCH_DESCRIPTION_OVERRIDES: ClassVar[dict[str, str]] = {}
842
984
 
985
+ @staticmethod
986
+ def _lite_docstring_tokens() -> dict[str, str]:
987
+ """Operator-tuned values interpolated into the lite descriptions.
988
+
989
+ ``_LITE_DOCSTRINGS`` is a static ``ClassVar``, but not every full
990
+ description it replaces is static: ``ha_manage_backup``'s is an
991
+ f-string built in ``register_backup_tools()`` that interpolates
992
+ ``_get_backup_hint_text()`` from ``BACKUP_HINT``
993
+ (strong/normal/weak). Hard-coding one wording into the lite text
994
+ would make that setting a silent no-op whenever lite mode is on —
995
+ the add-on UI would confirm a Backup-hint change that changed
996
+ nothing. So the lite values carry ``{token}`` placeholders and are
997
+ resolved here, reading the same env var at the same point in
998
+ startup the full description does.
999
+ """
1000
+ # Local import: same lazy-seam pattern as backup_manager's
1001
+ # _normalize_config_for_roundtrip import. It does NOT keep the tools
1002
+ # package off the module-load path — server.py already imports
1003
+ # .tools.helpers at module scope — what it avoids is pulling in
1004
+ # tools.backup, which is the actual reason to keep it local.
1005
+ from .tools.backup import _get_backup_hint_text
1006
+
1007
+ return {"backup_hint_text": _get_backup_hint_text()}
1008
+
1009
+ @classmethod
1010
+ def _resolve_lite_docstrings(cls) -> dict[str, str]:
1011
+ """Return ``_LITE_DOCSTRINGS`` with its placeholders filled in.
1012
+
1013
+ Uses ``str.replace`` rather than ``str.format`` so the descriptions
1014
+ stay free to contain literal braces (Jinja examples, JSON) without
1015
+ a stray ``{`` turning into a ``KeyError`` at startup. An unknown
1016
+ placeholder is left verbatim rather than raising —
1017
+ ``test_every_placeholder_in_the_map_is_resolvable`` is what catches a
1018
+ typo'd token, at test time instead of in a user's tool catalog.
1019
+ """
1020
+ tokens = cls._lite_docstring_tokens()
1021
+ resolved: dict[str, str] = {}
1022
+ for name, lite in cls._LITE_DOCSTRINGS.items():
1023
+ filled = lite
1024
+ for token, value in tokens.items():
1025
+ filled = filled.replace("{" + token + "}", value)
1026
+ resolved[name] = filled
1027
+ return resolved
1028
+
843
1029
  def _apply_lite_docstrings(self) -> None:
844
1030
  """Swap heavy tool descriptions for shorter variants if enabled.
845
1031
 
@@ -850,6 +1036,10 @@ class HomeAssistantSmartMCPServer:
850
1036
  ``ha_get_skill_guide``. Tools not in the
851
1037
  mapping pass through unchanged.
852
1038
 
1039
+ Descriptions are resolved through ``_resolve_lite_docstrings`` so
1040
+ operator-tuned text (``BACKUP_HINT``) survives the swap instead of
1041
+ being pinned to whatever wording the static map happened to carry.
1042
+
853
1043
  Emits a startup WARNING when enabled so non-addon users (Docker,
854
1044
  uvx, pip) see the trade-off in their logs — the addon UI surfaces
855
1045
  the same warning via the toggle description. A second WARNING is
@@ -884,7 +1074,7 @@ class HomeAssistantSmartMCPServer:
884
1074
 
885
1075
  try:
886
1076
  self.mcp.add_transform(
887
- LiteDocstringsTransform(replacements=self._LITE_DOCSTRINGS)
1077
+ LiteDocstringsTransform(replacements=self._resolve_lite_docstrings())
888
1078
  )
889
1079
  except Exception:
890
1080
  logger.exception("Failed to apply LiteDocstringsTransform")