ha-mcp-dev 7.6.0.dev650__tar.gz → 7.6.0.dev652__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 (140) hide show
  1. {ha_mcp_dev-7.6.0.dev650/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.6.0.dev652}/PKG-INFO +4 -2
  2. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/README.md +3 -1
  3. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/pyproject.toml +1 -2
  4. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/__main__.py +8 -3
  5. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/client/rest_client.py +1 -0
  6. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/client/websocket_client.py +2 -0
  7. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/client/websocket_listener.py +11 -3
  8. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/dashboard_screenshot/capture.py +2 -2
  9. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/dashboard_screenshot/provision.py +2 -1
  10. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/middleware.py +6 -5
  11. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/server.py +15 -4
  12. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/settings_ui.py +8 -8
  13. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/backup.py +11 -8
  14. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/device_control.py +143 -108
  15. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/helpers.py +6 -4
  16. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_deep.py +1 -0
  17. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_entities.py +8 -0
  18. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_overview.py +3 -0
  19. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_addons.py +11 -7
  20. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_areas.py +5 -0
  21. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_blueprints.py +91 -56
  22. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_bug_report.py +4 -0
  23. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_calendar.py +3 -0
  24. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_categories.py +6 -0
  25. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_code.py +10 -10
  26. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_automations.py +4 -1
  27. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_dashboards.py +11 -2
  28. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_helpers.py +4 -0
  29. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_scenes.py +10 -4
  30. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_scripts.py +2 -0
  31. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_energy.py +17 -20
  32. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_entities.py +3 -0
  33. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_filesystem.py +9 -1
  34. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_groups.py +3 -0
  35. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_hacs.py +3 -0
  36. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_history.py +3 -0
  37. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_integrations.py +12 -4
  38. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_labels.py +6 -0
  39. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_mcp_component.py +3 -0
  40. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_registry.py +5 -0
  41. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_resources.py +16 -0
  42. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_search.py +4 -0
  43. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_service.py +3 -0
  44. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_services.py +3 -0
  45. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_system.py +5 -0
  46. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_todo.py +5 -0
  47. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_traces.py +65 -34
  48. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_updates.py +3 -0
  49. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_utility.py +15 -5
  50. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_voice_assistant.py +144 -96
  51. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_yaml_config.py +2 -0
  52. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_zones.py +5 -0
  53. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/util_helpers.py +0 -15
  54. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/transforms/categorized_search.py +4 -2
  55. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/kill_signal_diagnostics.py +40 -6
  56. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652/src/ha_mcp_dev.egg-info}/PKG-INFO +4 -2
  57. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/LICENSE +0 -0
  58. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/MANIFEST.in +0 -0
  59. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/setup.cfg +0 -0
  60. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/__init__.py +0 -0
  61. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/_pypi_marker +0 -0
  62. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/_version.py +0 -0
  63. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/auth/__init__.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/auth/consent_form.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/auth/provider.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/backup_manager.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/client/__init__.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/client/supervisor_client.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/config.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/errors.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/__init__.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/approval_queue.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/evaluator.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/handlers.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/model.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/persistence.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/policy/value_sources.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/py.typed +0 -0
  80. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  81. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  82. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  83. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  84. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  85. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  86. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  87. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  88. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  89. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  90. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  91. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  92. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  93. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  94. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  95. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  96. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  97. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  98. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  99. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  100. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  101. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  102. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/settings.css +0 -0
  103. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/settings.js +0 -0
  104. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/smoke_test.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/__init__.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/auto_backup.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/enhanced.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/reference_validator.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/registry.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  118. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_camera.py +0 -0
  119. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  120. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  121. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/tools/validation_middleware.py +0 -0
  122. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/transforms/__init__.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/__init__.py +0 -0
  125. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/config_hash.py +0 -0
  126. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/data_paths.py +0 -0
  127. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/domain_handlers.py +0 -0
  128. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  129. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/operation_manager.py +0 -0
  130. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/python_sandbox.py +0 -0
  131. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/skill_loader.py +0 -0
  132. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp/utils/usage_logger.py +0 -0
  133. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  134. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  135. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  136. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  137. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  138. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/tests/__init__.py +0 -0
  139. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/tests/test_constants.py +0 -0
  140. {ha_mcp_dev-7.6.0.dev650 → ha_mcp_dev-7.6.0.dev652}/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.6.0.dev650
3
+ Version: 7.6.0.dev652
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
@@ -269,7 +269,9 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
269
269
 
270
270
  To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
271
271
 
272
- After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA MCP Tools**.
272
+ After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **Home Assistant MCP Server Custom Component**.
273
+
274
+ On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
273
275
 
274
276
  ### Install manually
275
277
 
@@ -239,7 +239,9 @@ These tools also require feature flags: `HAMCP_ENABLE_FILESYSTEM_TOOLS=true` (fi
239
239
 
240
240
  To add manually: open **HACS** > **Integrations** > three-dot menu > **Custom repositories** > add `https://github.com/homeassistant-ai/ha-mcp` (category: Integration) > **Download**.
241
241
 
242
- After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **HA MCP Tools**.
242
+ After installing, restart Home Assistant. Then open **Settings** > **Devices & Services** > **Add Integration** and search for **Home Assistant MCP Server Custom Component**.
243
+
244
+ On **Home Assistant OS / Supervised**, the integration offers to add the add-on repository and install and start the **Home Assistant MCP Server** add-on for you — no need to add the add-on repository by hand. On **Container / Core** installs (no Supervisor) there is no add-on; run the server via Docker or pip and the integration just sets up the file/YAML services.
243
245
 
244
246
  ### Install manually
245
247
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.6.0.dev650"
7
+ version = "7.6.0.dev652"
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"
@@ -132,7 +132,6 @@ ignore = [
132
132
  "RUF001", # ambiguous unicode — HA entity names use degree symbols etc
133
133
  "RUF003", # ambiguous unicode in comments
134
134
  "RUF010", # explicit f-string type conversion — str(x) is clearer than !s
135
- "F841", # unused variable — too many to fix now
136
135
  "RUF059", # unused unpacked variable
137
136
  "SIM118", # in-dict-keys — style preference
138
137
  "PIE810", # multiple-starts-ends-with — style preference
@@ -3,14 +3,15 @@
3
3
  import sys
4
4
 
5
5
  if sys.version_info < (3, 13): # noqa: UP036 — uvx can bypass requires-python and run on 3.12
6
- print(
6
+ # Write directly to stderr (not print) so this import-time version gate
7
+ # fires before any 3.13-only syntax in the rest of the module is parsed.
8
+ sys.stderr.write(
7
9
  f"ERROR: ha-mcp requires Python 3.13+, but you are running Python "
8
10
  f"{sys.version_info.major}.{sys.version_info.minor}.\n"
9
11
  "If using uvx, add '--python 3.13' to your config args:\n"
10
12
  ' "args": ["--python", "3.13", "--refresh", "ha-mcp@latest"]\n'
11
13
  "Or install Python 3.13: brew install python@3.13 (macOS) / "
12
- "sudo apt install python3.13 (Linux)",
13
- file=sys.stderr,
14
+ "sudo apt install python3.13 (Linux)\n"
14
15
  )
15
16
  sys.exit(1)
16
17
 
@@ -496,6 +497,8 @@ async def _cancel_tasks(*tasks: asyncio.Task) -> None:
496
497
  try:
497
498
  await task
498
499
  except asyncio.CancelledError:
500
+ # Expected: we just cancelled this task, swallow its
501
+ # CancelledError so remaining tasks still get awaited.
499
502
  pass
500
503
 
501
504
 
@@ -525,6 +528,8 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
525
528
  except TimeoutError:
526
529
  logger.warning("Server did not stop within timeout")
527
530
  except asyncio.CancelledError:
531
+ # Expected: we just cancelled server_task above; swallow its
532
+ # CancelledError so shutdown can proceed to cleanup.
528
533
  pass
529
534
 
530
535
  except asyncio.CancelledError:
@@ -711,6 +711,7 @@ class HomeAssistantClient:
711
711
  if isinstance(msg, str) and msg:
712
712
  message = msg
713
713
  except json.JSONDecodeError:
714
+ # Body wasn't a JSON envelope; fall back to raw text below.
714
715
  pass
715
716
  if not message:
716
717
  message = text_body.strip() or response.reason_phrase or "<empty body>"
@@ -352,6 +352,8 @@ class HomeAssistantWebSocketClient:
352
352
  try:
353
353
  await self.background_task
354
354
  except asyncio.CancelledError:
355
+ # Expected: we just cancelled the task above; swallow the
356
+ # propagated CancelledError so disconnect can finish cleanly.
355
357
  pass
356
358
  finally:
357
359
  self.background_task = None
@@ -87,6 +87,7 @@ class WebSocketListenerService:
87
87
  try:
88
88
  await self.listener_task
89
89
  except asyncio.CancelledError:
90
+ # Expected: awaiting a cancelled task re-raises CancelledError.
90
91
  pass
91
92
 
92
93
  if self.cleanup_task and not self.cleanup_task.done():
@@ -94,6 +95,7 @@ class WebSocketListenerService:
94
95
  try:
95
96
  await self.cleanup_task
96
97
  except asyncio.CancelledError:
98
+ # Expected: awaiting a cancelled task re-raises CancelledError.
97
99
  pass
98
100
 
99
101
  # Remove event handler if WebSocket client exists
@@ -164,7 +166,9 @@ class WebSocketListenerService:
164
166
  if updated_ops:
165
167
  operations_updated = self.stats["operations_updated"]
166
168
  if isinstance(operations_updated, int):
167
- self.stats["operations_updated"] = operations_updated + len(updated_ops)
169
+ self.stats["operations_updated"] = operations_updated + len(
170
+ updated_ops
171
+ )
168
172
  logger.info(f"Updated {len(updated_ops)} operations for {entity_id}")
169
173
 
170
174
  except (RuntimeError, ConnectionError, OSError) as e:
@@ -307,7 +311,6 @@ _listener_lock: asyncio.Lock | None = None
307
311
  async def get_listener_service() -> WebSocketListenerService:
308
312
  """Get the global WebSocket listener service instance."""
309
313
  global _listener_service, _listener_lock
310
- import asyncio
311
314
 
312
315
  current_loop = asyncio.get_event_loop()
313
316
 
@@ -373,7 +376,12 @@ class WebSocketContextManager:
373
376
  self.service = service
374
377
  return service
375
378
 
376
- async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: object) -> None:
379
+ async def __aexit__(
380
+ self,
381
+ exc_type: type[BaseException] | None,
382
+ exc_val: BaseException | None,
383
+ exc_tb: object,
384
+ ) -> None:
377
385
  """Stop WebSocket listener."""
378
386
  if self.service:
379
387
  await self.service.stop()
@@ -142,14 +142,14 @@ async def capture_dashboard_png(
142
142
  context={"engine_url": engine},
143
143
  suggestions=[
144
144
  "Ensure the Puppet screenshot add-on (or sidecar) is "
145
- "installed and running",
145
+ + "installed and running",
146
146
  # Puppet restarts itself when navigation fails, so a
147
147
  # missing/invalid token shows up as a dropped connection
148
148
  # rather than an HTTP error.
149
149
  f"If it is running, its access token is likely missing or "
150
150
  f"invalid — {TOKEN_HINT}",
151
151
  "Check HAMCP_DASHBOARD_SCREENSHOT_ENGINE_URL on "
152
- "Docker/Container deployments",
152
+ + "Docker/Container deployments",
153
153
  ],
154
154
  )
155
155
  )
@@ -96,10 +96,11 @@ async def resolve_engine_url() -> str:
96
96
  suggestions=[
97
97
  "Use HA OS / Supervised and install the screenshot engine add-on",
98
98
  "Or run the engine as a sidecar and set "
99
- "HAMCP_DASHBOARD_SCREENSHOT_ENGINE_URL",
99
+ + "HAMCP_DASHBOARD_SCREENSHOT_ENGINE_URL",
100
100
  ],
101
101
  )
102
102
  )
103
+ raise AssertionError("unreachable: raise_tool_error always raises")
103
104
 
104
105
 
105
106
  async def _discover_engine_url_via_supervisor() -> str:
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import logging
6
6
  from collections.abc import Callable
7
7
  from datetime import UTC, datetime
8
- from typing import Any
8
+ from typing import Any, NoReturn
9
9
 
10
10
  import anyio
11
11
  from anyio.to_thread import run_sync as run_in_thread
@@ -149,6 +149,7 @@ class PolicyMiddleware(Middleware):
149
149
  name,
150
150
  )
151
151
  self._raise_pending_error(pending, rule)
152
+ return None # py/mixed-returns: explicit terminal; error handlers above always raise (NoReturn), unreachable
152
153
 
153
154
  async def _wait_for_decision(
154
155
  self,
@@ -176,7 +177,7 @@ class PolicyMiddleware(Middleware):
176
177
  await pending.wait()
177
178
 
178
179
  @staticmethod
179
- def _raise_denied_error() -> None:
180
+ def _raise_denied_error() -> NoReturn:
180
181
  raise_tool_error(
181
182
  create_error_response(
182
183
  ErrorCode.USER_DENIED,
@@ -189,7 +190,7 @@ class PolicyMiddleware(Middleware):
189
190
 
190
191
  def _raise_pending_error(
191
192
  self, pending: PendingApproval, rule: Rule | None = None
192
- ) -> None:
193
+ ) -> NoReturn:
193
194
  # Time-remaining, not total TTL: an LLM that re-calls a minute
194
195
  # before expiry should see "~60s left", not the original 300s.
195
196
  remaining = max(
@@ -216,9 +217,9 @@ class PolicyMiddleware(Middleware):
216
217
  "with the same arguments after the user approves.",
217
218
  suggestions=[
218
219
  "Tell the user to open the Tool Security Policies tab in "
219
- "the ha-mcp settings UI and approve the pending request.",
220
+ + "the ha-mcp settings UI and approve the pending request.",
220
221
  "Re-call this tool with the same arguments after the user "
221
- "approves.",
222
+ + "approves.",
222
223
  ],
223
224
  context=context,
224
225
  )
@@ -47,6 +47,20 @@ _OLD_SKILL_TOOL_ALIASES = (
47
47
  "If you were going to call any of those, call this instead."
48
48
  )
49
49
 
50
+ # Hint shipped at the top of ha_get_skill_guide responses that deliver
51
+ # best-practice skill content directly. Smart clients that fetch the reference
52
+ # files proactively via this tool would otherwise still receive duplicate
53
+ # canonical content from the per-call write-tool attach — this hint tells them
54
+ # to opt out so the same body doesn't ride along again on every subsequent
55
+ # write. Defined here (its only consumer) rather than in util_helpers.
56
+ _SKILL_GUIDE_MANDATORYBPS_HINT = (
57
+ "You now have this best-practice reference in your context. "
58
+ "Pass `MandatoryBPS=false` on subsequent write-tool calls in this "
59
+ "session (ha_config_set_automation / _script / _scene / _helper / "
60
+ "_dashboard / _yaml) to avoid re-receiving the canonical reference "
61
+ "files inline."
62
+ )
63
+
50
64
 
51
65
  # Server icon configuration using GitHub-hosted images
52
66
  # These icons are bundled in packaging/mcpb/ and also available via GitHub raw URLs
@@ -1447,10 +1461,7 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
1447
1461
  # parsing the (potentially large) content body. Scoped to the
1448
1462
  # best-practice skill because that's the one the write-tool
1449
1463
  # MandatoryBPS param gates; other skills (if any) are unrelated.
1450
- from .tools.util_helpers import (
1451
- _HA_BEST_PRACTICES_SKILL_NAME,
1452
- _SKILL_GUIDE_MANDATORYBPS_HINT,
1453
- )
1464
+ from .tools.util_helpers import _HA_BEST_PRACTICES_SKILL_NAME
1454
1465
 
1455
1466
  response: dict[str, Any] = {}
1456
1467
  if skill == _HA_BEST_PRACTICES_SKILL_NAME:
@@ -1703,9 +1703,9 @@ def build_settings_handlers(
1703
1703
  ),
1704
1704
  suggestions=[
1705
1705
  "Include enable_beta_features=true in the same save "
1706
- "payload as the sub-flag(s).",
1706
+ + "payload as the sub-flag(s).",
1707
1707
  "Or turn on the master 'Enable beta features' toggle "
1708
- "first, then enable the sub-flag(s).",
1708
+ + "first, then enable the sub-flag(s).",
1709
1709
  ],
1710
1710
  context={"rejected": beta_sub_writes},
1711
1711
  ),
@@ -1874,10 +1874,10 @@ def build_settings_handlers(
1874
1874
  "Supervisor helper returned ok=False with no error",
1875
1875
  suggestions=[
1876
1876
  "Check the Home Assistant Supervisor logs and "
1877
- "the add-on logs for the underlying failure.",
1877
+ + "the add-on logs for the underlying failure.",
1878
1878
  "Report this at "
1879
- "https://github.com/homeassistant-ai/ha-mcp/issues "
1880
- "if it persists — this indicates an internal bug.",
1879
+ + "https://github.com/homeassistant-ai/ha-mcp/issues "
1880
+ + "if it persists — this indicates an internal bug.",
1881
1881
  ],
1882
1882
  ),
1883
1883
  status_code=500,
@@ -2575,10 +2575,10 @@ def build_settings_handlers(
2575
2575
  "Supervisor helper returned ok=False with no error",
2576
2576
  suggestions=[
2577
2577
  "Check the Home Assistant Supervisor logs and "
2578
- "the add-on logs for the underlying failure.",
2578
+ + "the add-on logs for the underlying failure.",
2579
2579
  "Report this at "
2580
- "https://github.com/homeassistant-ai/ha-mcp/issues "
2581
- "if it persists — this indicates an internal bug.",
2580
+ + "https://github.com/homeassistant-ai/ha-mcp/issues "
2581
+ + "if it persists — this indicates an internal bug.",
2582
2582
  ],
2583
2583
  ),
2584
2584
  status_code=500,
@@ -517,6 +517,7 @@ async def create_backup(
517
517
  context={"tool": "create_backup"},
518
518
  suggestions=["Check Home Assistant connection and backup configuration"],
519
519
  )
520
+ return None # unreachable: exception_to_structured_error always raises
520
521
  finally:
521
522
  # Always disconnect WebSocket — narrow to transport errors; a
522
523
  # programming error during cleanup should still surface.
@@ -714,6 +715,7 @@ async def restore_backup(
714
715
  context={"tool": "restore_backup", "backup_id": backup_id},
715
716
  suggestions=["Check Home Assistant connection and backup availability"],
716
717
  )
718
+ return None # unreachable: exception_to_structured_error always raises
717
719
  finally:
718
720
  # Always disconnect WebSocket — narrow to transport errors; a
719
721
  # programming error during cleanup should still surface.
@@ -726,6 +728,7 @@ async def restore_backup(
726
728
  type(err).__name__,
727
729
  err,
728
730
  )
731
+ return None # py/mixed-returns: explicit terminal; error handlers above always raise (NoReturn), unreachable
729
732
 
730
733
 
731
734
  # Valid (scope, action) combinations. Anything outside this set is
@@ -953,13 +956,13 @@ def register_backup_tools(
953
956
  create_error_response(
954
957
  ErrorCode.RESOURCE_NOT_FOUND,
955
958
  f"Could not snapshot {dom}:{eid} — entity not found "
956
- "or fetch returned no config",
959
+ + "or fetch returned no config",
957
960
  context={"domain": dom, "entity_id": eid},
958
961
  suggestions=[
959
962
  "Verify the entity exists via the matching "
960
- "ha_config_get_* tool first",
963
+ + "ha_config_get_* tool first",
961
964
  "For helpers, pass domain='helper_<helper_type>' "
962
- "(e.g. 'helper_input_boolean')",
965
+ + "(e.g. 'helper_input_boolean')",
963
966
  ],
964
967
  )
965
968
  )
@@ -1053,13 +1056,13 @@ def register_backup_tools(
1053
1056
  context={"backup_name": bname, "action": "restore"},
1054
1057
  suggestions=[
1055
1058
  "Verify the entity referenced by the backup still "
1056
- "exists; restore re-POSTs to its current registry "
1057
- "key",
1059
+ + "exists; restore re-POSTs to its current registry "
1060
+ + "key",
1058
1061
  "Compare the captured schema vs current HA — HA "
1059
- "minor versions occasionally drop/rename fields",
1062
+ + "minor versions occasionally drop/rename fields",
1060
1063
  "Inspect the snapshot YAML via "
1061
- "ha_manage_backup(scope='edits', action='view', "
1062
- "backup_name=...)",
1064
+ + "ha_manage_backup(scope='edits', action='view', "
1065
+ + "backup_name=...)",
1063
1066
  ],
1064
1067
  )
1065
1068
  return {