ha-mcp-dev 8.0.0.dev2228__tar.gz → 8.0.0.dev2230__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 (199) hide show
  1. {ha_mcp_dev-8.0.0.dev2228/src/ha_mcp_dev.egg-info → ha_mcp_dev-8.0.0.dev2230}/PKG-INFO +1 -1
  2. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/pyproject.toml +1 -1
  3. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/de.json +1 -1
  4. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/es.json +1 -1
  5. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/fr.json +1 -1
  6. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/it.json +1 -1
  7. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/ru.json +1 -1
  8. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/zh-Hans.json +1 -1
  9. ha_mcp_dev-8.0.0.dev2230/src/ha_mcp/tools/config_entry_flow.py +376 -0
  10. ha_mcp_dev-8.0.0.dev2230/src/ha_mcp/tools/config_entry_flow_form.py +694 -0
  11. ha_mcp_dev-8.0.0.dev2230/src/ha_mcp/tools/config_entry_flow_menu.py +185 -0
  12. ha_mcp_dev-8.0.0.dev2230/src/ha_mcp/tools/config_entry_flow_walker.py +728 -0
  13. ha_mcp_dev-8.0.0.dev2230/src/ha_mcp/tools/hacs_registration.py +393 -0
  14. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_config_helpers.py +24 -13
  15. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_dev.py +17 -1
  16. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_hacs.py +214 -392
  17. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_integrations.py +5 -2
  18. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
  19. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp_dev.egg-info/SOURCES.txt +4 -0
  20. ha_mcp_dev-8.0.0.dev2228/src/ha_mcp/tools/config_entry_flow.py +0 -1718
  21. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/LICENSE +0 -0
  22. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/MANIFEST.in +0 -0
  23. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/README.md +0 -0
  24. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/setup.cfg +0 -0
  25. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/__init__.py +0 -0
  26. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/__main__.py +0 -0
  27. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/_pypi_marker +0 -0
  28. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/_version.py +0 -0
  29. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/auth/__init__.py +0 -0
  30. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/auth/consent_form.py +0 -0
  31. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/auth/provider.py +0 -0
  32. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/backup_manager.py +0 -0
  33. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/browser_landing.py +0 -0
  34. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/client/__init__.py +0 -0
  35. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/client/rest_client.py +0 -0
  36. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/client/supervisor_client.py +0 -0
  37. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/client/websocket_client.py +0 -0
  38. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/client/websocket_listener.py +0 -0
  39. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/config.py +0 -0
  40. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  41. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  42. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  43. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  44. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  45. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  46. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/errors.py +0 -0
  47. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/llm_exposure.py +0 -0
  48. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/__init__.py +0 -0
  49. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/approval_queue.py +0 -0
  50. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/evaluator.py +0 -0
  51. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/handlers.py +0 -0
  52. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/middleware.py +0 -0
  53. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/model.py +0 -0
  54. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/persistence.py +0 -0
  55. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/policy/value_sources.py +0 -0
  56. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/py.typed +0 -0
  57. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/read_only.py +0 -0
  58. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  59. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  60. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  61. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  62. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  63. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  64. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  65. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  66. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  67. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  68. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  69. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  70. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  71. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  72. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  73. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  74. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  75. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  76. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  77. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  78. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  79. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  80. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  81. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  82. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  83. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/server.py +0 -0
  84. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/__init__.py +0 -0
  85. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  86. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  87. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  88. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  89. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  90. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  91. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  92. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  93. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  94. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_theme.py +0 -0
  95. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  96. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/README.md +0 -0
  97. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/locales/en.json +0 -0
  98. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/settings.css +0 -0
  99. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/settings.html +0 -0
  100. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/settings_ui/settings.js +0 -0
  101. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/smoke_test.py +0 -0
  102. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  103. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/strict_bps.py +0 -0
  104. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/__init__.py +0 -0
  105. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/auto_backup.py +0 -0
  106. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/backup.py +0 -0
  107. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  108. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/best_practice_result.py +0 -0
  109. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/best_practice_variables.py +0 -0
  110. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/component_api.py +0 -0
  111. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/component_config_reads.py +0 -0
  112. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/component_devices.py +0 -0
  113. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/component_registries.py +0 -0
  114. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  115. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/device_control.py +0 -0
  116. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/helpers.py +0 -0
  117. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/__init__.py +0 -0
  118. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/base.py +0 -0
  119. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/matter.py +0 -0
  120. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/thread.py +0 -0
  121. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  122. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/radio/zwave.py +0 -0
  123. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/reference_validator.py +0 -0
  124. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/registry.py +0 -0
  125. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  126. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  127. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  128. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  129. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  130. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  131. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  132. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  133. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  134. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  135. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_addons.py +0 -0
  136. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_areas.py +0 -0
  137. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  138. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  139. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_calendar.py +0 -0
  140. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_camera.py +0 -0
  141. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_categories.py +0 -0
  142. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_code.py +0 -0
  143. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  144. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  145. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  146. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  147. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  148. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_energy.py +0 -0
  149. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_entities.py +0 -0
  150. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  151. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_groups.py +0 -0
  152. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_history.py +0 -0
  153. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_labels.py +0 -0
  154. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_radio.py +0 -0
  155. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_registry.py +0 -0
  156. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_resources.py +0 -0
  157. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_search.py +0 -0
  158. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_service.py +0 -0
  159. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_services.py +0 -0
  160. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_system.py +0 -0
  161. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_themes.py +0 -0
  162. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_todo.py +0 -0
  163. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_traces.py +0 -0
  164. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_updates.py +0 -0
  165. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_utility.py +0 -0
  166. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  167. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  168. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  169. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/tools_zones.py +0 -0
  170. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/util_helpers.py +0 -0
  171. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/tools/validation_middleware.py +0 -0
  172. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/transforms/__init__.py +0 -0
  173. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/transforms/categorized_search.py +0 -0
  174. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  175. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/transport_security.py +0 -0
  176. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/update_check.py +0 -0
  177. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/__init__.py +0 -0
  178. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/config_hash.py +0 -0
  179. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/config_write_lock.py +0 -0
  180. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/data_paths.py +0 -0
  181. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/domain_handlers.py +0 -0
  182. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  183. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  184. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/operation_manager.py +0 -0
  185. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/python_sandbox.py +0 -0
  186. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/skill_loader.py +0 -0
  187. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/utils/usage_logger.py +0 -0
  188. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/visibility/__init__.py +0 -0
  189. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/visibility/enforcement.py +0 -0
  190. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/visibility/model.py +0 -0
  191. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/visibility/persistence.py +0 -0
  192. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp/visibility/resolver.py +0 -0
  193. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  194. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  195. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  196. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  197. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/tests/__init__.py +0 -0
  198. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/tests/test_constants.py +0 -0
  199. {ha_mcp_dev-8.0.0.dev2228 → ha_mcp_dev-8.0.0.dev2230}/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.0.0.dev2228
3
+ Version: 8.0.0.dev2230
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.0.0.dev2228"
7
+ version = "8.0.0.dev2230"
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
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "HACS verwalten",
738
- "description": "Installiert, aktualisiert und fügt benutzerdefinierte HACS-Repositories hinzu."
738
+ "description": "Verwalte HACS (Home Assistant Community Store) — installiere/aktualisiere, entferne oder füge benutzerdefinierte Repositories hinzu."
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "Assist-Pipeline verwalten",
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "Gestionar HACS",
738
- "description": "Gestiona HACS: instala o actualiza elementos y añade repositorios personalizados."
738
+ "description": "Gestionar HACS (Home Assistant Community Store) — instalar/actualizar, eliminar o añadir repositorios personalizados."
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "Gestionar canalización de Assist",
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "Gérer HACS",
738
- "description": "Installe, met à jour et ajoute des dépôts HACS personnalisés."
738
+ "description": "Gérer HACS (Home Assistant Community Store) — installer/mettre à jour, supprimer ou ajouter des dépôts personnalisés."
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "Gérer le pipeline Assist",
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "Gestisci HACS",
738
- "description": "Gestisce HACS (Home Assistant Community Store): installa o aggiorna elementi e aggiunge repository personalizzati."
738
+ "description": "Gestisci HACS (Home Assistant Community Store) — installare/aggiornare, rimuovere o aggiungere repository personalizzati."
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "Gestisci le pipeline di Assist",
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "Управлять HACS",
738
- "description": "Устанавливать, обновлять и добавлять пользовательские репозитории HACS."
738
+ "description": "Управление HACS (Магазин сообщества Home Assistant) — устанавливать/обновлять, удалять или добавлять пользовательские репозитории."
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "Управлять конвейером Assist",
@@ -735,7 +735,7 @@
735
735
  },
736
736
  "ha_manage_hacs": {
737
737
  "title": "管理 HACS",
738
- "description": "管理 HACS(Home Assistant 社区商店)——安装/更新或添加自定义仓库。"
738
+ "description": "管理 HACS (Home Assistant 社区商店) — 安装/更新、移除或添加自定义存储库。"
739
739
  },
740
740
  "ha_manage_pipeline": {
741
741
  "title": "管理 Assist 管线",
@@ -0,0 +1,376 @@
1
+ """
2
+ Config Entry Flow API machinery for Home Assistant MCP server.
3
+
4
+ This module provides the shared machinery for creating and updating
5
+ config-entry-based helpers (template, group, utility_meter, etc.) via the
6
+ Config Entry Flow API.
7
+
8
+ The create/update entry point is the unified ha_config_set_helper tool in
9
+ tools_config_helpers.py, which routes to create_flow_helper / update_flow_helper
10
+ for the 15 helper types listed in FLOW_HELPER_TYPES.
11
+
12
+ The same flow walkers drive every other config-entry surface, not just
13
+ helpers: ``ha_set_integration`` creates entries for arbitrary domains through
14
+ ``create_config_entry`` and edits them through ``update_config_entry_options``,
15
+ and ``ha_config_set_helper(helper_type="config_subentry")`` drives subentry
16
+ flows through ``set_config_subentry``.
17
+
18
+ The step machinery those entry points drive lives in three sibling modules,
19
+ imported in one direction only (menu <- form <- walker <- here):
20
+
21
+ - ``config_entry_flow_menu``: menu selection keys and menu-step handling
22
+ - ``config_entry_flow_form``: form-step schema consumption and reuse tracking
23
+ - ``config_entry_flow_walker``: step submission, HA error translation, flow
24
+ introspection, and the two flow walkers
25
+ """
26
+
27
+ import asyncio
28
+ import logging
29
+ from typing import Any, Literal
30
+
31
+ from ..errors import ErrorCode, create_error_response
32
+ from .config_entry_flow_form import _extract_schema_field_names
33
+ from .config_entry_flow_walker import (
34
+ _FlowType,
35
+ _handle_config_subentry_flow_steps,
36
+ _handle_flow_steps,
37
+ )
38
+ from .helpers import raise_tool_error
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+ # 15 helpers that use Config Entry Flow API (Issue #324).
43
+ SUPPORTED_HELPERS = Literal[
44
+ "template",
45
+ "group",
46
+ "utility_meter",
47
+ "derivative",
48
+ "min_max",
49
+ "threshold",
50
+ "integration",
51
+ "statistics",
52
+ "trend",
53
+ "random",
54
+ "filter",
55
+ "tod",
56
+ "generic_thermostat",
57
+ "switch_as_x",
58
+ "generic_hygrostat",
59
+ ]
60
+
61
+ # Value-set form of SUPPORTED_HELPERS for runtime routing checks.
62
+ # Exported for import by tools_config_helpers.ha_config_set_helper.
63
+ FLOW_HELPER_TYPES: frozenset[str] = frozenset(
64
+ {
65
+ "template",
66
+ "group",
67
+ "utility_meter",
68
+ "derivative",
69
+ "min_max",
70
+ "threshold",
71
+ "integration",
72
+ "statistics",
73
+ "trend",
74
+ "random",
75
+ "filter",
76
+ "tod",
77
+ "generic_thermostat",
78
+ "switch_as_x",
79
+ "generic_hygrostat",
80
+ }
81
+ )
82
+
83
+
84
+ # ---------------------------------------------------------------------------
85
+ # Module-level flow machinery
86
+ #
87
+ # These functions are shared by the unified ha_config_set_helper tool in
88
+ # tools_config_helpers.py. They take a client instance as an explicit
89
+ # parameter so the same logic can be used from any caller.
90
+ # ---------------------------------------------------------------------------
91
+
92
+
93
+ async def set_config_subentry(
94
+ client: Any,
95
+ entry_id: str,
96
+ subentry_type: str,
97
+ config_dict: dict[str, Any],
98
+ *,
99
+ subentry_id: str | None = None,
100
+ show_advanced_options: bool | None = None,
101
+ ) -> dict[str, Any]:
102
+ """Create or reconfigure a config subentry via its flow.
103
+
104
+ Presence of ``subentry_id`` is the discriminator: omitted creates a new
105
+ subentry, provided reconfigures that existing subentry.
106
+ ``show_advanced_options`` is a no-op on HA 2026.6+ and kept only for older
107
+ HA versions pending removal before HA 2027.6.
108
+ """
109
+ flow_result = await client.start_config_subentry_flow(
110
+ entry_id,
111
+ subentry_type,
112
+ subentry_id=subentry_id,
113
+ show_advanced_options=show_advanced_options,
114
+ )
115
+ flow_id = flow_result.get("flow_id")
116
+
117
+ if not flow_id:
118
+ raise_tool_error(
119
+ create_error_response(
120
+ ErrorCode.SERVICE_CALL_FAILED,
121
+ "Failed to start config subentry flow",
122
+ suggestions=[
123
+ "Use ha_get_integration(include_subentries=True) to confirm "
124
+ "the parent entry and available subentry metadata.",
125
+ ],
126
+ context={
127
+ "entry_id": entry_id,
128
+ "subentry_type": subentry_type,
129
+ "subentry_id": subentry_id,
130
+ "details": flow_result,
131
+ },
132
+ )
133
+ )
134
+
135
+ try:
136
+ result = await _handle_config_subentry_flow_steps(
137
+ client,
138
+ flow_id,
139
+ flow_result,
140
+ config_dict,
141
+ is_reconfigure=subentry_id is not None,
142
+ )
143
+ except Exception:
144
+ try:
145
+ await asyncio.wait_for(
146
+ client.abort_config_subentry_flow(flow_id), timeout=5.0
147
+ )
148
+ except Exception as abort_err:
149
+ logger.warning(
150
+ "Failed to abort config subentry flow %s after error: %s",
151
+ flow_id,
152
+ abort_err,
153
+ )
154
+ raise
155
+
156
+ response = {
157
+ "success": True,
158
+ "entry_id": entry_id,
159
+ "subentry_type": subentry_type,
160
+ "subentry_id": subentry_id,
161
+ "operation": result["operation"],
162
+ "flow_result": result["flow_result"],
163
+ "message": f"Config subentry {result['operation']} successfully",
164
+ }
165
+ if result.get("warnings"):
166
+ response["warnings"] = result["warnings"]
167
+ return response
168
+
169
+
170
+ async def get_user_step_field_names(client: Any, helper_type: str) -> set[str] | None:
171
+ """Return field names in the user-step form schema for ``helper_type``.
172
+
173
+ Starts a config flow, peeks at the initial step's ``data_schema``,
174
+ and immediately aborts the flow. Used to decide whether to fold the
175
+ top-level ``name`` parameter into the form payload — some helpers
176
+ (e.g. ``switch_as_x``) take their entity name from the source switch
177
+ and reject ``name`` as an extra key.
178
+
179
+ Returns:
180
+ A set of field names if the initial step is a form. ``None`` if
181
+ the flow type is not introspectable from the top step (menu or
182
+ unexpected) — callers should fall back to the legacy behaviour
183
+ in that case to avoid regressing menu helpers (template, group).
184
+ Also returns ``None`` if the introspection itself fails; the
185
+ subsequent real flow will surface the error in context.
186
+ """
187
+ flow_id = None
188
+ try:
189
+ flow_result = await client.start_config_flow(helper_type)
190
+ flow_id = flow_result.get("flow_id")
191
+ if flow_result.get("type") != _FlowType.FORM:
192
+ return None
193
+ return _extract_schema_field_names(flow_result.get("data_schema"))
194
+ except Exception as e:
195
+ logger.debug(f"Schema introspection failed for {helper_type}: {e}")
196
+ return None
197
+ finally:
198
+ if flow_id:
199
+ try:
200
+ await asyncio.wait_for(client.abort_config_flow(flow_id), timeout=5.0)
201
+ except Exception as abort_err:
202
+ logger.warning(
203
+ f"Failed to abort introspection flow {flow_id}: {abort_err}"
204
+ )
205
+
206
+
207
+ async def update_config_entry_options(
208
+ client: Any,
209
+ entry_id: str,
210
+ config_dict: dict[str, Any],
211
+ *,
212
+ expected_domain: str | None = None,
213
+ noun: str = "integration",
214
+ ) -> dict[str, Any]:
215
+ """Update an existing config entry via its options flow.
216
+
217
+ When ``expected_domain`` is provided, verifies the entry's domain matches
218
+ it first (the helper path passes the helper_type; the generic
219
+ ``ha_set_integration`` path passes ``None`` to accept any domain). Starts
220
+ an options flow, walks the flow steps, and returns the result. Aborts the
221
+ flow on error. ``noun`` only affects response wording.
222
+ """
223
+ config_entry = await client.get_config_entry(entry_id)
224
+ actual_domain = config_entry.get("domain")
225
+ if expected_domain is not None and actual_domain != expected_domain:
226
+ raise_tool_error(
227
+ create_error_response(
228
+ ErrorCode.VALIDATION_INVALID_PARAMETER,
229
+ f"entry_id '{entry_id}' belongs to domain '{actual_domain}', not '{expected_domain}'",
230
+ suggestions=[
231
+ f"Use ha_get_integration(domain='{expected_domain}') to find valid entry IDs",
232
+ ],
233
+ context={
234
+ "entry_id": entry_id,
235
+ "expected": expected_domain,
236
+ "actual": actual_domain,
237
+ },
238
+ )
239
+ )
240
+
241
+ flow_result = await client.start_options_flow(entry_id)
242
+ flow_id = flow_result.get("flow_id")
243
+
244
+ if not flow_id:
245
+ raise_tool_error(
246
+ create_error_response(
247
+ ErrorCode.SERVICE_CALL_FAILED,
248
+ "Failed to start options flow",
249
+ suggestions=[
250
+ "Check that the entry supports options (supports_options=true)"
251
+ ],
252
+ context={"entry_id": entry_id, "details": flow_result},
253
+ )
254
+ )
255
+
256
+ try:
257
+ result = await _handle_flow_steps(
258
+ client,
259
+ flow_id,
260
+ flow_result,
261
+ config_dict,
262
+ submit_fn=client.submit_options_flow_step,
263
+ helper_type=expected_domain,
264
+ )
265
+ except Exception:
266
+ try:
267
+ await asyncio.wait_for(client.abort_options_flow(flow_id), timeout=5.0)
268
+ except Exception as abort_err:
269
+ logger.warning(
270
+ f"Failed to abort options flow {flow_id} after error: {abort_err}"
271
+ )
272
+ raise
273
+
274
+ entry = result["entry"].get("result", {})
275
+ response = {
276
+ "success": True,
277
+ "entry_id": entry_id,
278
+ "title": entry.get("title"),
279
+ "domain": actual_domain,
280
+ "message": f"{actual_domain} {noun} updated successfully",
281
+ "updated": True,
282
+ }
283
+ if result.get("warnings"):
284
+ response["warnings"] = result["warnings"]
285
+ return response
286
+
287
+
288
+ async def update_flow_helper(
289
+ client: Any,
290
+ helper_type: str,
291
+ config_dict: dict[str, Any],
292
+ entry_id: str,
293
+ ) -> dict[str, Any]:
294
+ """Update an existing flow-based helper via its options flow.
295
+
296
+ Verifies the entry domain matches helper_type, starts an options flow,
297
+ walks the flow steps, and returns the result. Aborts the flow on error.
298
+ """
299
+ return await update_config_entry_options(
300
+ client,
301
+ entry_id,
302
+ config_dict,
303
+ expected_domain=helper_type,
304
+ noun="helper",
305
+ )
306
+
307
+
308
+ async def create_config_entry(
309
+ client: Any,
310
+ domain: str,
311
+ config_dict: dict[str, Any],
312
+ *,
313
+ noun: str = "integration",
314
+ ) -> dict[str, Any]:
315
+ """Create a config entry by driving ``domain``'s config flow.
316
+
317
+ Starts a config flow, walks the flow steps (menus and multi-step forms),
318
+ and returns the result. Aborts the flow on error. ``noun`` only affects
319
+ response wording.
320
+ """
321
+ flow_result = await client.start_config_flow(domain)
322
+ flow_id = flow_result.get("flow_id")
323
+
324
+ if not flow_id:
325
+ raise_tool_error(
326
+ create_error_response(
327
+ ErrorCode.SERVICE_CALL_FAILED,
328
+ "Failed to start config flow",
329
+ suggestions=[
330
+ f"Check that the {noun} domain exists and Home Assistant is reachable"
331
+ ],
332
+ context={"domain": domain, "details": flow_result},
333
+ )
334
+ )
335
+
336
+ try:
337
+ result = await _handle_flow_steps(
338
+ client,
339
+ flow_id,
340
+ flow_result,
341
+ config_dict,
342
+ helper_type=domain,
343
+ )
344
+ except Exception:
345
+ try:
346
+ await asyncio.wait_for(client.abort_config_flow(flow_id), timeout=5.0)
347
+ except Exception as abort_err:
348
+ logger.warning(
349
+ f"Failed to abort config flow {flow_id} after error: {abort_err}"
350
+ )
351
+ raise
352
+
353
+ entry = result["entry"].get("result", {})
354
+ response = {
355
+ "success": True,
356
+ "entry_id": entry.get("entry_id"),
357
+ "title": entry.get("title"),
358
+ "domain": domain,
359
+ "message": f"{domain} {noun} created successfully",
360
+ }
361
+ if result.get("warnings"):
362
+ response["warnings"] = result["warnings"]
363
+ return response
364
+
365
+
366
+ async def create_flow_helper(
367
+ client: Any,
368
+ helper_type: str,
369
+ config_dict: dict[str, Any],
370
+ ) -> dict[str, Any]:
371
+ """Create a new flow-based helper via the config flow.
372
+
373
+ Starts a config flow, walks the flow steps, and returns the result.
374
+ Aborts the flow on error.
375
+ """
376
+ return await create_config_entry(client, helper_type, config_dict, noun="helper")