ha-mcp-dev 7.6.0.dev677__tar.gz → 7.7.0.dev681__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.dev677/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.7.0.dev681}/PKG-INFO +1 -1
  2. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/pyproject.toml +1 -1
  3. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_integrations.py +77 -57
  4. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  5. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/tests/test_constants.py +1 -1
  6. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/LICENSE +0 -0
  7. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/MANIFEST.in +0 -0
  8. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/README.md +0 -0
  9. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/setup.cfg +0 -0
  10. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/__init__.py +0 -0
  11. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/__main__.py +0 -0
  12. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/_pypi_marker +0 -0
  13. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/_version.py +0 -0
  14. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/auth/__init__.py +0 -0
  15. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/auth/consent_form.py +0 -0
  16. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/auth/provider.py +0 -0
  17. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/backup_manager.py +0 -0
  18. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/client/__init__.py +0 -0
  19. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/client/rest_client.py +0 -0
  20. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/client/supervisor_client.py +0 -0
  21. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/client/websocket_client.py +0 -0
  22. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/client/websocket_listener.py +0 -0
  23. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/config.py +0 -0
  24. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  25. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  26. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  27. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/errors.py +0 -0
  28. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/__init__.py +0 -0
  29. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/approval_queue.py +0 -0
  30. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/evaluator.py +0 -0
  31. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/handlers.py +0 -0
  32. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/middleware.py +0 -0
  33. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/model.py +0 -0
  34. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/persistence.py +0 -0
  35. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/policy/value_sources.py +0 -0
  36. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/py.typed +0 -0
  37. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  38. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  39. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  40. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  41. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  42. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  43. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  44. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  45. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  46. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  47. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  48. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  49. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  50. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  51. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  52. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  53. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  54. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  55. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  56. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  57. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  58. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  59. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/server.py +0 -0
  60. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/settings.css +0 -0
  61. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/settings.js +0 -0
  62. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/settings_ui.py +0 -0
  63. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/smoke_test.py +0 -0
  64. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  65. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/__init__.py +0 -0
  66. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/auto_backup.py +0 -0
  67. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/backup.py +0 -0
  68. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  69. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/device_control.py +0 -0
  70. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/enhanced.py +0 -0
  71. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/helpers.py +0 -0
  72. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/reference_validator.py +0 -0
  73. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/registry.py +0 -0
  74. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  75. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  76. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  77. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  78. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  79. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  80. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  81. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  82. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  83. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_addons.py +0 -0
  84. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_areas.py +0 -0
  85. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  86. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  87. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_calendar.py +0 -0
  88. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_camera.py +0 -0
  89. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_categories.py +0 -0
  90. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_code.py +0 -0
  91. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  92. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  93. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_entry_flow.py +0 -0
  94. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  95. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  96. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  97. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  98. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_energy.py +0 -0
  99. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_entities.py +0 -0
  100. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  101. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_groups.py +0 -0
  102. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_hacs.py +0 -0
  103. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_history.py +0 -0
  104. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_labels.py +0 -0
  105. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  106. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_registry.py +0 -0
  107. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_resources.py +0 -0
  108. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_search.py +0 -0
  109. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_service.py +0 -0
  110. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_services.py +0 -0
  111. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_system.py +0 -0
  112. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_todo.py +0 -0
  113. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_traces.py +0 -0
  114. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_updates.py +0 -0
  115. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_utility.py +0 -0
  116. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  117. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  118. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/tools_zones.py +0 -0
  119. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/util_helpers.py +0 -0
  120. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/tools/validation_middleware.py +0 -0
  121. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/transforms/__init__.py +0 -0
  122. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/transforms/categorized_search.py +0 -0
  123. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  124. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/__init__.py +0 -0
  125. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/config_hash.py +0 -0
  126. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/data_paths.py +0 -0
  127. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/domain_handlers.py +0 -0
  128. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  129. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  130. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/operation_manager.py +0 -0
  131. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/python_sandbox.py +0 -0
  132. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/skill_loader.py +0 -0
  133. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp/utils/usage_logger.py +0 -0
  134. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  135. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  136. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  137. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  138. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  139. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/tests/__init__.py +0 -0
  140. {ha_mcp_dev-7.6.0.dev677 → ha_mcp_dev-7.7.0.dev681}/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.dev677
3
+ Version: 7.7.0.dev681
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.6.0.dev677"
7
+ version = "7.7.0.dev681"
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"
@@ -102,13 +102,18 @@ assert set(get_args(HelperTypeLiteral)) == (
102
102
  def options_from_form_flow(flow: dict[str, Any]) -> dict[str, Any]:
103
103
  """Extract ``{field_name: current_value}`` from a form-type OptionsFlow.
104
104
 
105
- Reads each ``data_schema`` entry's ``default`` key, falling back to
106
- ``value`` (constant-type fields ship ``value`` instead of ``default``)
107
- and then ``description.suggested_value`` (UI-created template, group,
108
- utility_meter, and other flow-based helpers stash the current value
109
- there voluptuous renders ``suggested_value=...`` into the
110
- ``description`` sub-object, not as a top-level field key). Fields with
111
- a missing or ``None`` value are skipped.
105
+ Reads each ``data_schema`` entry's ``description.suggested_value``
106
+ first: HA's ``add_suggested_values_to_schema`` injects the entry's
107
+ *persisted* option there (voluptuous renders ``suggested_value=...``
108
+ into the ``description`` sub-object, not as a top-level field key),
109
+ and it is what the HA UI renders as the current value. Falls back to
110
+ ``default`` (the static schema default a brand-new form would show)
111
+ and then ``value`` (constant-type fields ship ``value`` instead of
112
+ ``default``). A field can carry both ``suggested_value`` and
113
+ ``default`` at once — e.g. a group helper's ``hide_members`` stored as
114
+ ``True`` over a schema default of ``False`` — and the stored value
115
+ must win (issue #1575). Fields with a missing or ``None`` value are
116
+ skipped.
112
117
  """
113
118
  out: dict[str, Any] = {}
114
119
  # Defensive: HA should always return a list of dict fields, but guard
@@ -123,11 +128,12 @@ def options_from_form_flow(flow: dict[str, Any]) -> dict[str, Any]:
123
128
  name = field.get("name")
124
129
  if name is None:
125
130
  continue
126
- value = field.get("default", field.get("value"))
131
+ value = None
132
+ description = field.get("description")
133
+ if isinstance(description, dict):
134
+ value = description.get("suggested_value")
127
135
  if value is None:
128
- description = field.get("description")
129
- if isinstance(description, dict):
130
- value = description.get("suggested_value")
136
+ value = field.get("default", field.get("value"))
131
137
  if value is not None:
132
138
  out[name] = value
133
139
  return out
@@ -138,7 +144,10 @@ async def fetch_entry_options_with_status(
138
144
  ) -> tuple[dict[str, Any], bool]:
139
145
  """Read a config entry's ``options`` and report whether the probe succeeded.
140
146
 
141
- Identical mechanics to :func:`fetch_entry_options` but returns
147
+ Starts the entry's OptionsFlow, harvests ``{name: current_value}`` from
148
+ its first-step form via :func:`options_from_form_flow` (the persisted
149
+ option from ``description.suggested_value``, falling back to the schema
150
+ ``default``), and aborts the flow so it doesn't sit half-open. Returns
142
151
  ``(options, ok)`` so callers can tell a probe *failure* apart from a
143
152
  genuinely-empty options form — both yield ``{}`` for ``options``, but
144
153
  ``ok`` is:
@@ -147,20 +156,23 @@ async def fetch_entry_options_with_status(
147
156
  fields: a genuinely-empty options form is a successful read).
148
157
  - ``False`` when the OptionsFlow could not be read into options: the flow
149
158
  raised, or its first step was not a form (a menu / abort / create_entry),
150
- so no defaults could be harvested.
159
+ so no options could be harvested.
151
160
 
152
- The flag lets bulk fan-out callers (``smart_search``) surface ``partial``
153
- when an options-flow probe fails mid-search instead of silently scoring the
154
- helper on title/domain only the per-entry analog of the per-type/per-
155
- dashboard backend-failure signals. The abort in ``finally`` is cleanup; a
156
- failed abort does not flip ``ok`` (the options were already harvested).
161
+ The flag lets callers surface degraded reads instead of passing ``{}``
162
+ off as real options: ``smart_search`` flips ``partial`` when a probe
163
+ fails mid-search, and ``ha_get_integration`` attaches a ``warnings``
164
+ entry on its single-entry and list responses. The abort in ``finally``
165
+ is cleanup; a failed abort does not flip ``ok`` (the options were
166
+ already harvested).
157
167
 
158
168
  Home Assistant does not expose ``ConfigEntry.options`` through any
159
169
  read-only REST or WebSocket endpoint — ``/api/config/config_entries/entry``
160
170
  deliberately omits the field. The closest approximation that the HA UI
161
- itself uses is the ``default`` values populated into the OptionsFlow's
162
- first-step ``data_schema``: integrations build that schema from the
163
- existing options dict, so the defaults match the persisted state.
171
+ itself uses is the OptionsFlow's first-step ``data_schema``: HA injects
172
+ the persisted options into each field's ``description.suggested_value``
173
+ (via ``add_suggested_values_to_schema``), which
174
+ :func:`options_from_form_flow` prefers over the static schema
175
+ ``default`` (issue #1575).
164
176
 
165
177
  Probe failures log at ``warning`` (so breakage of a deliberate
166
178
  single-entry probe is discoverable) unless ``quiet=True``, which demotes
@@ -181,7 +193,7 @@ async def fetch_entry_options_with_status(
181
193
  if flow_type != "form":
182
194
  log_probe_failure(
183
195
  f"OptionsFlow for {entry_id} returned type={flow_type!r}, "
184
- f"not a form — cannot extract option defaults"
196
+ f"not a form — cannot extract options"
185
197
  )
186
198
  return {}, False
187
199
  return options_from_form_flow(flow), True
@@ -201,25 +213,6 @@ async def fetch_entry_options_with_status(
201
213
  )
202
214
 
203
215
 
204
- async def fetch_entry_options(
205
- client: Any, entry_id: str, *, quiet: bool = False
206
- ) -> dict[str, Any]:
207
- """Read the current ``options`` for a config entry via its OptionsFlow.
208
-
209
- Starts the flow, harvests ``{name: default}`` from the first step, and
210
- aborts the flow so it doesn't sit half-open. Returns ``{}`` on any failure
211
- (unsupported entry, non-form first step such as a menu, init/abort errors)
212
- so callers can treat the return as the canonical "options" field without
213
- further checks.
214
-
215
- Thin wrapper over :func:`fetch_entry_options_with_status` for callers that
216
- only need the options dict and not the success flag (e.g. the
217
- ``ha_remove_helpers_integrations`` / ``ha_get_integration`` config readout).
218
- """
219
- options, _ok = await fetch_entry_options_with_status(client, entry_id, quiet=quiet)
220
- return options
221
-
222
-
223
216
  async def _get_entry_id_for_flow_helper(
224
217
  client: Any,
225
218
  helper_type: str,
@@ -714,19 +707,33 @@ class IntegrationTools:
714
707
 
715
708
  # Surface `options` on every per-entry response (HA's REST endpoint
716
709
  # omits the field). For entries with supports_options=True we probe
717
- # via OptionsFlow — see `fetch_entry_options`. When include_schema
718
- # is also requested, `_fetch_options_schema` below populates options
719
- # from the same flow init so we don't pay for two round-trips.
710
+ # via OptionsFlow — see `fetch_entry_options_with_status`. When
711
+ # include_schema is also requested, `_fetch_options_schema` below
712
+ # populates options from the same flow init so we don't pay for
713
+ # two round-trips.
714
+ probe_warnings: list[str] = []
720
715
  if isinstance(result, dict):
721
716
  result.setdefault("options", {})
722
717
  if result.get("supports_options") and not include_schema:
723
- result["options"] = await self._fetch_entry_options(entry_id)
718
+ options, probe_ok = await fetch_entry_options_with_status(
719
+ self._client, entry_id
720
+ )
721
+ result["options"] = options
722
+ if not probe_ok:
723
+ probe_warnings.append(
724
+ f"options probe failed for {entry_id}: the "
725
+ "OptionsFlow could not be read, so 'options' may "
726
+ "be incomplete — empty options does not mean the "
727
+ "entry has none"
728
+ )
724
729
 
725
730
  resp: dict[str, Any] = {
726
731
  "success": True,
727
732
  "entry_id": entry_id,
728
733
  "entry": result,
729
734
  }
735
+ if probe_warnings:
736
+ resp["warnings"] = probe_warnings
730
737
 
731
738
  # Surface the effective Python logger level for this integration
732
739
  # so users can confirm logger.set_level changes took effect.
@@ -862,15 +869,6 @@ class IntegrationTools:
862
869
  """Class-method alias for :func:`options_from_form_flow`."""
863
870
  return options_from_form_flow(flow)
864
871
 
865
- async def _fetch_entry_options(self, entry_id: str) -> dict[str, Any]:
866
- """Instance wrapper around :func:`fetch_entry_options`.
867
-
868
- Kept so existing call sites (and the ``include_schema`` path) read
869
- naturally as ``self._fetch_entry_options(...)``; the probe logic and
870
- full rationale live on the module-level function.
871
- """
872
- return await fetch_entry_options(self._client, entry_id)
873
-
874
872
  async def _fetch_options_schema(self, entry_id: str, resp: dict[str, Any]) -> None:
875
873
  """Start an options flow to read the schema, then abort it.
876
874
 
@@ -903,6 +901,12 @@ class IntegrationTools:
903
901
  f"Failed to fetch options schema for {entry_id}: "
904
902
  f"{type(schema_err).__name__}: {schema_err}"
905
903
  )
904
+ schema_warnings: list[str] = resp.setdefault("warnings", [])
905
+ schema_warnings.append(
906
+ f"options schema probe failed for {entry_id}: "
907
+ f"{type(schema_err).__name__} — 'options_schema' is missing "
908
+ "and 'options' may be incomplete"
909
+ )
906
910
  finally:
907
911
  if flow_id:
908
912
  try:
@@ -949,11 +953,14 @@ class IntegrationTools:
949
953
 
950
954
  # `_format_entry` is sync and cannot probe the OptionsFlow; options
951
955
  # are filled in by a second async pass below for entries that
952
- # advertise supports_options=True. See `fetch_entry_options`.
956
+ # advertise supports_options=True. See `fetch_entry_options_with_status`.
953
957
  formatted_entries = [
954
958
  self._format_entry(entry, include_opts, logger_levels) for entry in entries
955
959
  ]
956
960
 
961
+ # quiet=True: per-entry probe failures are aggregated into a response
962
+ # warning below instead of one log line each (bulk fan-out).
963
+ probe_failures: list[str] = []
957
964
  if include_opts:
958
965
  options_targets = [
959
966
  e for e in formatted_entries if e.get("supports_options")
@@ -961,13 +968,19 @@ class IntegrationTools:
961
968
  if options_targets:
962
969
  fetched = await asyncio.gather(
963
970
  *(
964
- self._fetch_entry_options(e["entry_id"])
971
+ fetch_entry_options_with_status(
972
+ self._client, e["entry_id"], quiet=True
973
+ )
965
974
  for e in options_targets
966
975
  ),
967
976
  return_exceptions=False,
968
977
  )
969
- for entry, opts in zip(options_targets, fetched, strict=True):
978
+ for entry, (opts, probe_ok) in zip(
979
+ options_targets, fetched, strict=True
980
+ ):
970
981
  entry["options"] = opts
982
+ if not probe_ok:
983
+ probe_failures.append(entry["entry_id"])
971
984
 
972
985
  # Apply search filter if query provided
973
986
  if query and query.strip():
@@ -996,6 +1009,13 @@ class IntegrationTools:
996
1009
  }
997
1010
  if domain:
998
1011
  result_data["domain_filter"] = domain.strip().lower()
1012
+ if probe_failures:
1013
+ result_data["warnings"] = [
1014
+ f"options probe failed for {len(probe_failures)} "
1015
+ f"entr{'y' if len(probe_failures) == 1 else 'ies'} "
1016
+ f"({', '.join(probe_failures)}) — their 'options' may be "
1017
+ "incomplete; empty options does not mean an entry has none"
1018
+ ]
999
1019
  return result_data
1000
1020
 
1001
1021
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.6.0.dev677
3
+ Version: 7.7.0.dev681
4
4
  Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
5
5
  Author-email: Julien <github@qc-h.net>
6
6
  License: MIT
@@ -20,7 +20,7 @@ NON_ADMIN_TEST_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIzNzkyOTc
20
20
  # Home Assistant Docker image for E2E/performance/UAT tests.
21
21
  # Keep in sync with .github/workflows/e2e-tests.yml and pr.yml.
22
22
  # renovate: datasource=docker depName=ghcr.io/home-assistant/home-assistant
23
- HA_TEST_IMAGE = "ghcr.io/home-assistant/home-assistant:2026.5.4"
23
+ HA_TEST_IMAGE = "ghcr.io/home-assistant/home-assistant:2026.6.1"
24
24
 
25
25
  # Test user credentials (for UI access)
26
26
  TEST_USER = "mcp"