ha-mcp-dev 7.14.2.dev2162__tar.gz → 7.14.2.dev2167__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 (193) hide show
  1. {ha_mcp_dev-7.14.2.dev2162/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2167}/PKG-INFO +2 -2
  2. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/README.md +1 -1
  3. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/pyproject.toml +1 -1
  4. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/__init__.py +1 -1
  5. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/rest_client.py +0 -77
  6. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/websocket_listener.py +0 -123
  7. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/config.py +2 -32
  8. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/value_sources.py +0 -4
  9. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/server.py +4 -54
  10. ha_mcp_dev-7.14.2.dev2167/src/ha_mcp/settings_ui/locales/README.md +96 -0
  11. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/de.json +0 -2
  12. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/en.json +0 -2
  13. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/fr.json +0 -2
  14. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/ru.json +0 -2
  15. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -2
  16. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.js +4 -5
  17. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_deep.py +2 -2
  18. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_dashboards.py +1 -51
  19. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_search.py +8 -8
  20. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_utility.py +2 -2
  21. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/domain_handlers.py +0 -172
  22. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/fuzzy_search.py +0 -40
  23. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/operation_manager.py +0 -60
  24. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
  25. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -1
  26. ha_mcp_dev-7.14.2.dev2162/src/ha_mcp/settings_ui/locales/README.md +0 -18
  27. ha_mcp_dev-7.14.2.dev2162/src/ha_mcp/tools/enhanced.py +0 -190
  28. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/LICENSE +0 -0
  29. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/MANIFEST.in +0 -0
  30. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/setup.cfg +0 -0
  31. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/__main__.py +0 -0
  32. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/_pypi_marker +0 -0
  33. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/_version.py +0 -0
  34. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/__init__.py +0 -0
  35. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/consent_form.py +0 -0
  36. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/provider.py +0 -0
  37. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/backup_manager.py +0 -0
  38. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/browser_landing.py +0 -0
  39. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/__init__.py +0 -0
  40. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/supervisor_client.py +0 -0
  41. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/websocket_client.py +0 -0
  42. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  43. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  44. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
  45. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
  46. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  47. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
  48. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/errors.py +0 -0
  49. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/llm_exposure.py +0 -0
  50. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/__init__.py +0 -0
  51. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/approval_queue.py +0 -0
  52. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/evaluator.py +0 -0
  53. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/handlers.py +0 -0
  54. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/middleware.py +0 -0
  55. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/model.py +0 -0
  56. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/persistence.py +0 -0
  57. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/py.typed +0 -0
  58. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/read_only.py +0 -0
  59. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  60. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  61. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  62. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  63. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  64. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  65. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  66. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  67. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  68. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  69. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  70. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  71. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
  72. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  73. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
  74. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  75. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  76. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  77. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  78. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  79. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  80. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  81. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
  82. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  83. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  84. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/__init__.py +0 -0
  85. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
  86. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
  87. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
  88. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
  89. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
  90. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
  91. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_i18n.py +0 -0
  92. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_persistence.py +0 -0
  93. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
  94. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_theme.py +0 -0
  95. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
  96. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/es.json +0 -0
  97. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.css +0 -0
  98. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.html +0 -0
  99. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/smoke_test.py +0 -0
  100. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  101. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/strict_bps.py +0 -0
  102. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/__init__.py +0 -0
  103. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/auto_backup.py +0 -0
  104. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/backup.py +0 -0
  105. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  106. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_api.py +0 -0
  107. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_config_reads.py +0 -0
  108. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_devices.py +0 -0
  109. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_registries.py +0 -0
  110. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
  111. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  112. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/device_control.py +0 -0
  113. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/helpers.py +0 -0
  114. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/__init__.py +0 -0
  115. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/base.py +0 -0
  116. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/matter.py +0 -0
  117. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/thread.py +0 -0
  118. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/zigbee.py +0 -0
  119. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/zwave.py +0 -0
  120. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/reference_validator.py +0 -0
  121. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/registry.py +0 -0
  122. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  123. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  124. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  125. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  126. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  127. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  128. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  129. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  130. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  131. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_addons.py +0 -0
  132. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_areas.py +0 -0
  133. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  134. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  135. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_calendar.py +0 -0
  136. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_camera.py +0 -0
  137. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_categories.py +0 -0
  138. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_code.py +0 -0
  139. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  140. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  141. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  142. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  143. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  144. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_dev.py +0 -0
  145. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_energy.py +0 -0
  146. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_entities.py +0 -0
  147. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  148. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_groups.py +0 -0
  149. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_hacs.py +0 -0
  150. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_history.py +0 -0
  151. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_integrations.py +0 -0
  152. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_labels.py +0 -0
  153. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_radio.py +0 -0
  154. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_registry.py +0 -0
  155. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_resources.py +0 -0
  156. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_service.py +0 -0
  157. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_services.py +0 -0
  158. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_system.py +0 -0
  159. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_themes.py +0 -0
  160. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_todo.py +0 -0
  161. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_traces.py +0 -0
  162. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_updates.py +0 -0
  163. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  164. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  165. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
  166. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_zones.py +0 -0
  167. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/util_helpers.py +0 -0
  168. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/validation_middleware.py +0 -0
  169. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/__init__.py +0 -0
  170. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/categorized_search.py +0 -0
  171. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  172. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transport_security.py +0 -0
  173. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/update_check.py +0 -0
  174. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/__init__.py +0 -0
  175. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/config_hash.py +0 -0
  176. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/config_write_lock.py +0 -0
  177. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/data_paths.py +0 -0
  178. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  179. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/python_sandbox.py +0 -0
  180. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/skill_loader.py +0 -0
  181. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/usage_logger.py +0 -0
  182. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/__init__.py +0 -0
  183. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/enforcement.py +0 -0
  184. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/model.py +0 -0
  185. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/persistence.py +0 -0
  186. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/resolver.py +0 -0
  187. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  188. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  189. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
  190. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  191. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/tests/__init__.py +0 -0
  192. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/tests/test_constants.py +0 -0
  193. {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/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.14.2.dev2162
3
+ Version: 7.14.2.dev2167
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
@@ -370,7 +370,7 @@ If your model can't see the tools or your Home Assistant, it may be getting hand
370
370
 
371
371
  ### Enable search-based discovery
372
372
 
373
- Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search_entities, ha_get_overview, ha_restart, etc.). All tools remain callable directly by name once discovered:
373
+ Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search, ha_get_overview, ha_report_issue, etc.). All tools remain callable directly by name once discovered:
374
374
 
375
375
  | Tool | Purpose |
376
376
  |------|---------|
@@ -337,7 +337,7 @@ If your model can't see the tools or your Home Assistant, it may be getting hand
337
337
 
338
338
  ### Enable search-based discovery
339
339
 
340
- Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search_entities, ha_get_overview, ha_restart, etc.). All tools remain callable directly by name once discovered:
340
+ Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search, ha_get_overview, ha_report_issue, etc.). All tools remain callable directly by name once discovered:
341
341
 
342
342
  | Tool | Purpose |
343
343
  |------|---------|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.14.2.dev2162"
7
+ version = "7.14.2.dev2167"
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
@@ -2,7 +2,7 @@
2
2
  Home Assistant MCP Server
3
3
 
4
4
  A Model Context Protocol server that provides complete control over Home Assistant
5
- through REST API and WebSocket integration with 20+ enhanced tools.
5
+ through REST API and WebSocket integration.
6
6
  """
7
7
 
8
8
  from ._version import get_version
@@ -412,28 +412,6 @@ class HomeAssistantClient:
412
412
  logger.debug(f"Fetching state for entity: {entity_id}")
413
413
  return await self._request("GET", f"/states/{entity_id}")
414
414
 
415
- async def set_entity_state(
416
- self, entity_id: str, state: str, attributes: dict[str, Any] | None = None
417
- ) -> dict[str, Any]:
418
- """
419
- Set entity state.
420
-
421
- Args:
422
- entity_id: Entity ID
423
- state: New state value
424
- attributes: Optional attributes dictionary
425
-
426
- Returns:
427
- Updated entity state
428
- """
429
- logger.debug(f"Setting state for entity {entity_id} to {state}")
430
-
431
- payload: dict[str, Any] = {"state": state}
432
- if attributes:
433
- payload["attributes"] = attributes
434
-
435
- return await self._request("POST", f"/states/{entity_id}", json=payload)
436
-
437
415
  async def call_service(
438
416
  self,
439
417
  domain: str,
@@ -490,41 +468,6 @@ class HomeAssistantClient:
490
468
  logger.debug("Fetching available services")
491
469
  return await self._request("GET", "/services")
492
470
 
493
- async def get_history(
494
- self,
495
- entity_id: str | None = None,
496
- start_time: str | None = None,
497
- end_time: str | None = None,
498
- ) -> list[list[dict[str, Any]]]:
499
- """
500
- Get historical data.
501
-
502
- Args:
503
- entity_id: Optional entity ID to filter
504
- start_time: Optional start time (ISO format)
505
- end_time: Optional end time (ISO format)
506
-
507
- Returns:
508
- Historical data
509
- """
510
- logger.debug(f"Fetching history for entity: {entity_id}")
511
-
512
- params = {}
513
- if start_time:
514
- params["start_time"] = start_time
515
- if end_time:
516
- params["end_time"] = end_time
517
-
518
- endpoint = "/history/period"
519
- if entity_id:
520
- endpoint += f"/{entity_id}"
521
-
522
- result = await self._request("GET", endpoint, params=params)
523
- if isinstance(result, list):
524
- return result
525
- else:
526
- return []
527
-
528
471
  async def get_logbook(
529
472
  self,
530
473
  entity_id: str | None = None,
@@ -962,15 +905,6 @@ class HomeAssistantClient:
962
905
  logger.error(f"Failed to connect to Home Assistant: {e}")
963
906
  return False, str(e)
964
907
 
965
- async def get_system_health(self) -> dict[str, Any]:
966
- """Get system health information."""
967
- logger.debug("Fetching system health")
968
- try:
969
- return await self._request("GET", "/system_health/info")
970
- except HomeAssistantAPIError:
971
- # System health might not be available in all HA instances
972
- return {"status": "unknown", "message": "System health not available"}
973
-
974
908
  # Automation Configuration Management
975
909
 
976
910
  async def _resolve_automation_id(self, identifier: str) -> str:
@@ -2097,14 +2031,3 @@ class HomeAssistantClient:
2097
2031
  status_code=405,
2098
2032
  ) from e
2099
2033
  raise
2100
-
2101
-
2102
- async def create_client() -> HomeAssistantClient:
2103
- """Create and return a new Home Assistant client."""
2104
- return HomeAssistantClient()
2105
-
2106
-
2107
- async def test_connection_with_config() -> tuple[bool, str | None]:
2108
- """Test connection using configuration settings."""
2109
- async with HomeAssistantClient() as client:
2110
- return await client.test_connection()
@@ -7,7 +7,6 @@ WebSocket events and updates operation status in real-time.
7
7
 
8
8
  import asyncio
9
9
  import logging
10
- from datetime import datetime
11
10
  from typing import Any
12
11
 
13
12
  from ..config import get_global_settings
@@ -29,13 +28,6 @@ class WebSocketListenerService:
29
28
  self.cleanup_task: asyncio.Task | None = None
30
29
  self.running = False
31
30
  self._event_loop: asyncio.AbstractEventLoop | None = None
32
- self.stats: dict[str, Any] = {
33
- "events_processed": 0,
34
- "operations_updated": 0,
35
- "connection_errors": 0,
36
- "last_event_time": None,
37
- "start_time": None,
38
- }
39
31
 
40
32
  async def start(self) -> bool:
41
33
  """Start the WebSocket listener service.
@@ -64,7 +56,6 @@ class WebSocketListenerService:
64
56
  self.cleanup_task = asyncio.create_task(self._periodic_cleanup())
65
57
 
66
58
  self.running = True
67
- self.stats["start_time"] = datetime.now()
68
59
 
69
60
  logger.info("WebSocket listener service started successfully")
70
61
  return True
@@ -141,11 +132,6 @@ class WebSocketListenerService:
141
132
  entity_id: str | None = None
142
133
  new_state: dict[str, Any] | None = None
143
134
  try:
144
- events_processed = self.stats["events_processed"]
145
- if isinstance(events_processed, int):
146
- self.stats["events_processed"] = events_processed + 1
147
- self.stats["last_event_time"] = datetime.now()
148
-
149
135
  # Extract event data — fields are nested under ``event["data"]``.
150
136
  event_data = event.get("data") or {}
151
137
  entity_id = event_data.get("entity_id")
@@ -164,11 +150,6 @@ class WebSocketListenerService:
164
150
  # Update pending operations
165
151
  updated_ops = update_pending_operations(entity_id, new_state)
166
152
  if updated_ops:
167
- operations_updated = self.stats["operations_updated"]
168
- if isinstance(operations_updated, int):
169
- self.stats["operations_updated"] = operations_updated + len(
170
- updated_ops
171
- )
172
153
  logger.info(f"Updated {len(updated_ops)} operations for {entity_id}")
173
154
 
174
155
  except (RuntimeError, ConnectionError, OSError) as e:
@@ -198,14 +179,8 @@ class WebSocketListenerService:
198
179
  ping_success = await self.websocket_client.ping()
199
180
  if not ping_success:
200
181
  logger.warning("WebSocket ping failed")
201
- connection_errors = self.stats["connection_errors"]
202
- if isinstance(connection_errors, int):
203
- self.stats["connection_errors"] = connection_errors + 1
204
182
  else:
205
183
  logger.warning("WebSocket connection lost")
206
- connection_errors = self.stats["connection_errors"]
207
- if isinstance(connection_errors, int):
208
- self.stats["connection_errors"] = connection_errors + 1
209
184
 
210
185
  # Try to reconnect
211
186
  try:
@@ -241,67 +216,6 @@ class WebSocketListenerService:
241
216
  logger.error(f"Cleanup task error: {e}")
242
217
  await asyncio.sleep(300)
243
218
 
244
- def get_status(self) -> dict[str, Any]:
245
- """Get service status and statistics.
246
-
247
- Returns:
248
- Dictionary with service status and statistics
249
- """
250
- uptime: float | None = None
251
- start_time = self.stats["start_time"]
252
- if isinstance(start_time, datetime):
253
- uptime = (datetime.now() - start_time).total_seconds()
254
-
255
- return {
256
- "running": self.running,
257
- "websocket_connected": (
258
- self.websocket_client.is_connected if self.websocket_client else False
259
- ),
260
- "uptime_seconds": uptime,
261
- "statistics": {
262
- **self.stats,
263
- "last_event_time": (
264
- self.stats["last_event_time"].isoformat()
265
- if isinstance(self.stats["last_event_time"], datetime)
266
- else None
267
- ),
268
- "start_time": (
269
- self.stats["start_time"].isoformat()
270
- if isinstance(self.stats["start_time"], datetime)
271
- else None
272
- ),
273
- },
274
- "operation_summary": self.operation_manager.get_operations_summary(),
275
- }
276
-
277
- async def force_reconnect(self) -> bool:
278
- """Force a WebSocket reconnection.
279
-
280
- Returns:
281
- True if reconnection successful
282
- """
283
- try:
284
- if self.websocket_client:
285
- self.websocket_client.remove_event_handler(
286
- "state_changed", self._handle_state_change
287
- )
288
-
289
- self.websocket_client = await get_websocket_client()
290
- await self.websocket_client.subscribe_events("state_changed")
291
- self.websocket_client.add_event_handler(
292
- "state_changed", self._handle_state_change
293
- )
294
-
295
- logger.info("Forced WebSocket reconnection successful")
296
- return True
297
-
298
- except Exception as e:
299
- logger.error(f"Forced reconnection failed: {e}")
300
- connection_errors = self.stats["connection_errors"]
301
- if isinstance(connection_errors, int):
302
- self.stats["connection_errors"] = connection_errors + 1
303
- return False
304
-
305
219
 
306
220
  # Global listener service instance
307
221
  _listener_service: WebSocketListenerService | None = None
@@ -353,40 +267,3 @@ async def stop_websocket_listener() -> None:
353
267
  if _listener_service:
354
268
  await _listener_service.stop()
355
269
  _listener_service = None
356
-
357
-
358
- async def get_listener_status() -> dict[str, Any]:
359
- """Get WebSocket listener service status."""
360
- service = await get_listener_service()
361
- return service.get_status()
362
-
363
-
364
- class WebSocketContextManager:
365
- """Context manager for WebSocket listener lifecycle."""
366
-
367
- def __init__(self) -> None:
368
- self.service: WebSocketListenerService | None = None
369
-
370
- async def __aenter__(self) -> WebSocketListenerService:
371
- """Start WebSocket listener."""
372
- service = await get_listener_service()
373
- success = await service.start()
374
- if not success:
375
- raise Exception("Failed to start WebSocket listener")
376
- self.service = service
377
- return service
378
-
379
- async def __aexit__(
380
- self,
381
- exc_type: type[BaseException] | None,
382
- exc_val: BaseException | None,
383
- exc_tb: object,
384
- ) -> None:
385
- """Stop WebSocket listener."""
386
- if self.service:
387
- await self.service.stop()
388
-
389
-
390
- def websocket_listener_context() -> WebSocketContextManager:
391
- """Create a WebSocket listener context manager."""
392
- return WebSocketContextManager()
@@ -63,10 +63,9 @@ class Settings(BaseSettings):
63
63
 
64
64
  # Tool configuration
65
65
  fuzzy_threshold: int = Field(60, alias="FUZZY_THRESHOLD")
66
- entity_search_limit: int = Field(20, alias="ENTITY_SEARCH_LIMIT")
67
66
 
68
67
  # Smart-search config-fetch time budgets (seconds). Bound how long
69
- # ha_search / ha_deep_search spends fetching automation/script/scene
68
+ # ha_search spends fetching automation/script/scene
70
69
  # definitions during the per-id fallback before reporting a partial
71
70
  # result. Surfaced in the Advanced settings panel (issue #1538) so
72
71
  # add-on users — who cannot set raw env vars — can tune them. Consumed
@@ -466,11 +465,6 @@ class Settings(BaseSettings):
466
465
  return int(val)
467
466
  return val
468
467
 
469
- @property
470
- def env_file_name(self) -> str:
471
- """Get the current environment file name."""
472
- return os.getenv("HAMCP_ENV_FILE", ".env")
473
-
474
468
  @field_validator("homeassistant_url")
475
469
  @classmethod
476
470
  def validate_homeassistant_url(cls, v: str) -> str:
@@ -580,28 +574,6 @@ def get_settings() -> Settings:
580
574
  return Settings() # type: ignore[call-arg]
581
575
 
582
576
 
583
- def validate_settings() -> tuple[bool, str | None]:
584
- """
585
- Validate settings and return (is_valid, error_message).
586
-
587
- Returns:
588
- tuple: (True, None) if valid, (False, error_message) if invalid
589
- """
590
- try:
591
- settings = get_settings()
592
-
593
- # Additional validation
594
- if not settings.homeassistant_url:
595
- return False, "Home Assistant URL is required"
596
-
597
- if not settings.homeassistant_token:
598
- return False, "Home Assistant token is required"
599
-
600
- return True, None
601
- except Exception as e:
602
- return False, str(e)
603
-
604
-
605
577
  # Runtime-editable feature flags surfaced in the /settings web UI
606
578
  # (issue #863). Each entry is (field_name, env_var_name, python_type).
607
579
  # The web UI's /api/settings/features GET/POST endpoints iterate this
@@ -811,7 +783,6 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
811
783
  AdvancedField("verify_ssl", "HA_VERIFY_SSL", bool, "operations", True),
812
784
  # Search & matching.
813
785
  AdvancedField("fuzzy_threshold", "FUZZY_THRESHOLD", int, "search", True),
814
- AdvancedField("entity_search_limit", "ENTITY_SEARCH_LIMIT", int, "search", True),
815
786
  # Smart-search config-fetch time budgets (#1538). Restart-required
816
787
  # (consumed as import-time constants in smart_search/_config.py).
817
788
  AdvancedField(
@@ -941,12 +912,11 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
941
912
  # Bounds present on the Settings field today (mirrored):
942
913
  # fuzzy_threshold (validator 0-100), code_mode_* (Field ge/le).
943
914
  # Bounds added purely as UI/POST guardrails (no Field constraint):
944
- # timeout, max_retries, entity_search_limit.
915
+ # timeout, max_retries.
945
916
  _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
946
917
  "timeout": (1, 600),
947
918
  "max_retries": (0, 20),
948
919
  "fuzzy_threshold": (0, 100),
949
- "entity_search_limit": (1, 1000),
950
920
  "automation_config_time_budget": (1.0, 600.0),
951
921
  "script_config_time_budget": (1.0, 600.0),
952
922
  "scene_config_time_budget": (1.0, 600.0),
@@ -50,10 +50,6 @@ _CACHE_TTL_SECONDS = 30.0
50
50
  _cache: dict[str, tuple[float, list[str]]] = {}
51
51
 
52
52
 
53
- def value_source_for(tool_name: str, arg_path: str) -> str | None:
54
- return VALUE_SOURCE_REGISTRY.get((tool_name, arg_path))
55
-
56
-
57
53
  def all_value_sources_for(tool_name: str) -> dict[str, str]:
58
54
  """Return {arg_path: value_source} for one tool — used by the UI to
59
55
  decide which paths render as dropdowns vs free-text."""
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import logging
13
13
  from pathlib import Path
14
- from typing import TYPE_CHECKING, Annotated, Any, ClassVar, cast
14
+ from typing import TYPE_CHECKING, Annotated, Any, ClassVar
15
15
 
16
16
  import yaml # type: ignore[import-untyped]
17
17
  from fastmcp import FastMCP
@@ -20,9 +20,7 @@ from pydantic import Field
20
20
 
21
21
  from .config import _PACKAGE_VERSION, get_global_settings
22
22
  from .errors import ErrorCode, create_error_response
23
- from .tools.enhanced import EnhancedToolsMixin
24
23
  from .tools.helpers import raise_tool_error
25
- from .tools.util_helpers import strip_internal_fields
26
24
  from .transforms import DEFAULT_PINNED_TOOLS
27
25
 
28
26
  if TYPE_CHECKING:
@@ -77,7 +75,7 @@ SERVER_ICONS = [
77
75
  ]
78
76
 
79
77
 
80
- class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
78
+ class HomeAssistantSmartMCPServer:
81
79
  """Home Assistant MCP Server with smart tools and fuzzy search.
82
80
 
83
81
  Uses lazy initialization to improve startup time:
@@ -196,9 +194,6 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
196
194
  # Register tools
197
195
  self.tools_registry.register_all_tools()
198
196
 
199
- # Register enhanced tools for first/second interaction success
200
- self.register_enhanced_tools()
201
-
202
197
  # Register bundled skills as MCP resources
203
198
  self._register_skills()
204
199
 
@@ -1789,51 +1784,6 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
1789
1784
  )
1790
1785
  return response
1791
1786
 
1792
- # Helper methods required by EnhancedToolsMixin
1793
-
1794
- async def smart_entity_search(
1795
- self, query: str, domain_filter: str | None = None, limit: int = 10
1796
- ) -> dict[str, Any]:
1797
- """Bridge method to existing smart search implementation."""
1798
- return cast(
1799
- dict[str, Any],
1800
- await self.smart_tools.smart_entity_search(
1801
- query=query, limit=limit, include_attributes=False
1802
- ),
1803
- )
1804
-
1805
- async def get_entity_state(self, entity_id: str) -> dict[str, Any]:
1806
- """Bridge method to existing entity state implementation."""
1807
- return await self.client.get_entity_state(entity_id)
1808
-
1809
- async def call_service(
1810
- self,
1811
- domain: str,
1812
- service: str,
1813
- entity_id: str | None = None,
1814
- data: dict | None = None,
1815
- ) -> list[dict[str, Any]] | dict[str, Any]:
1816
- """Bridge method to existing service call implementation."""
1817
- service_data = data or {}
1818
- if entity_id:
1819
- service_data["entity_id"] = entity_id
1820
- return await self.client.call_service(domain, service, service_data)
1821
-
1822
- async def get_entities_by_area(self, area_name: str) -> dict[str, Any]:
1823
- """Bridge method to existing area functionality.
1824
-
1825
- ``smart_tools.get_entities_by_area`` enriches per-entity dicts
1826
- with leading-underscore internals (``_hidden_by`` etc.) so
1827
- downstream search branches can apply the score penalty without
1828
- a second registry lookup. Strip them here so this public bridge
1829
- doesn't leak internals to MCP clients.
1830
- """
1831
- result = await self.smart_tools.get_entities_by_area(
1832
- area_query=area_name, group_by_domain=True
1833
- )
1834
- strip_internal_fields(result)
1835
- return cast(dict[str, Any], result)
1836
-
1837
1787
  async def start(self) -> None:
1838
1788
  """Start the Smart MCP server with async compatibility."""
1839
1789
  logger.info(
@@ -1853,8 +1803,8 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
1853
1803
  except Exception as e:
1854
1804
  logger.error(f"❌ Error testing connection: {e}")
1855
1805
 
1856
- # Log available tools count
1857
- logger.info("🔧 Smart server with enhanced tools loaded")
1806
+ # Log successful server initialization
1807
+ logger.info("🔧 Home Assistant MCP tools loaded")
1858
1808
 
1859
1809
  # Run the MCP server with async compatibility
1860
1810
  await self.mcp.run_async()
@@ -0,0 +1,96 @@
1
+ # Settings UI translations
2
+
3
+ The settings page discovers every `*.json` catalog in this directory. No Python
4
+ or JavaScript registration is required, and no packaging file needs editing —
5
+ the wheel, sdist and binary declarations all match this directory by pattern.
6
+
7
+ ## Adding a language
8
+
9
+ A language ships on **all four** translated surfaces or not at all, and one
10
+ Home Assistant language code names every file:
11
+
12
+ - `src/ha_mcp/settings_ui/locales/<code>.json` (this directory)
13
+ - `custom_components/ha_mcp_tools/translations/<code>.json`
14
+ - `homeassistant-addon/translations/<code>.yaml`
15
+ - `homeassistant-addon-dev/translations/<code>.yaml`
16
+
17
+ Adding only this catalog fails `test_every_locale_ships_on_every_surface`. Also
18
+ add the new code to the locale list in the repository-root `AGENTS.md`
19
+ § Translations — that list is pinned by
20
+ `test_agents_md_lists_every_shipped_locale`.
21
+
22
+ Only the first of those four files is described below, and the other three
23
+ carry content rules of their own: the component catalog needs every `en.json`
24
+ key with identical `{placeholders}` and no extra ones, and each add-on YAML
25
+ needs a `name` and a `description` for every `schema:` key of *that* flavor's
26
+ `config.yaml`, with nothing left behind for a key the schema no longer
27
+ declares. The two flavors declare different schemas, so neither YAML is a copy
28
+ of the other. The repository-root `AGENTS.md` § Translations states all of
29
+ this; a contributor who writes only this catalog goes red on the other three.
30
+
31
+ **Start from a translated catalog, not from `en.json`.** English for the tool
32
+ titles and descriptions comes from the tool definitions at runtime, so `en.json`
33
+ ships `tools` and `tool_groups` empty; a copy of it is missing both sections
34
+ that this catalog is required to carry.
35
+
36
+ **Read the other surfaces before you word a switch.** Wherever the same English
37
+ text is shipped from more than one catalog, your wording has to be byte-identical
38
+ in all of them. That is not only the add-on-options-versus-settings-UI axis: the
39
+ two add-on flavors describe most of the same options, so a good part of the
40
+ pinned parity is stable-against-dev, with no settings-UI text involved at all.
41
+ Translating one surface at a time is exactly how one option ends up with two
42
+ different sentences.
43
+
44
+ ## Catalog sections
45
+
46
+ - `meta.native_name`: language name shown in the selector. It must be
47
+ non-empty, must not repeat English's own name, and must differ from every
48
+ other catalog's — a copied catalog that keeps the name it was copied from
49
+ fails `test_native_names_name_their_own_language`, because the picker would
50
+ then offer one label twice.
51
+ - `meta.dir`: `ltr` or `rtl`. Omitting it means `ltr`; any other value is
52
+ rejected when the catalog loads.
53
+ - `messages`: interface labels, help text, notices, and runtime messages. Keys
54
+ may be omitted — English is the per-key fallback at runtime — but see the
55
+ share limit below before leaving a catalog half-finished.
56
+ - `tool_groups`: one entry per renderable MCP tool tag, keyed by the English
57
+ tag. Not optional, and exact: no key more and none fewer.
58
+ - `tools`: `title` and `description` per tool, keyed by the stable MCP tool
59
+ name. The key set is not optional and exact in the same way; either field on
60
+ its own may be left out, but a missing one counts as untranslated against the
61
+ share limit below.
62
+
63
+ Keep the keys and `{placeholders}` unchanged in every section.
64
+
65
+ `messages` values carry two further rules, both enforced when the catalog
66
+ loads rather than by a named test — breaking one raises a `ValueError` at
67
+ import, so the failure names the file but arrives as a broken test module:
68
+
69
+ - The only inline markup the page can restore is `<code>`, `<strong>`, `</a>`
70
+ and `<a href="#" data-panel-link="...">`, spelled exactly that way. Any other
71
+ tag — `<b>`, `<CODE>`, `<code >` — is rejected.
72
+ - A `data-panel-link` target must be a tab the settings page declares, and a
73
+ translated message must link to the same tabs as its English source, with
74
+ the same multiplicity. The order may differ, so a translation is free to
75
+ reorder two links to suit its grammar.
76
+
77
+ ## What CI checks
78
+
79
+ - Every surface carries the same set of language codes.
80
+ - `tool_groups` and `tools` name exactly the renderable groups and tools — a
81
+ tool added to the codebase turns every locale red in the PR that adds it.
82
+ - At most 5% of this catalog's `messages`, and 5% of its `tools` texts, may be
83
+ byte-identical to English or missing outright. Both add-on flavors are held to
84
+ the same 5%; the component catalogs allow 15%, because they carry product
85
+ names as keys of their own. A single tool whose `title` *and* `description` are
86
+ both still English fails by name however small the share.
87
+ - One wording per English string across surfaces, wherever the same English text
88
+ is shipped from more than one catalog. The failure names every group that
89
+ disagrees. Where a group has a settings UI member, that is the wording the
90
+ other surfaces follow today; a group carried only by the two add-on flavors
91
+ has no such member, so there pick one wording and use it in both.
92
+ - The English each translation was written against is hashed in
93
+ `tests/src/unit/locale_source_baseline.json`, so a later edit to an English
94
+ string turns the locales red rather than leaving them silently stale. Adding a
95
+ language does not change any English source, so no baseline regeneration is
96
+ needed for it.
@@ -371,8 +371,6 @@
371
371
  "advanced.verify_ssl.help": "Deaktiviere TLS-Überprüfung nur in vertrauenswürdigen Netzwerken. Neustart erforderlich.",
372
372
  "advanced.fuzzy_threshold.label": "Fuzzy-Such-Schwellenwert",
373
373
  "advanced.fuzzy_threshold.help": "Niedrigere Werte erlauben lockerere Entitätsabgleichung. Bereich 0-100.",
374
- "advanced.entity_search_limit.label": "Entitätssuchergebnis-Limit",
375
- "advanced.entity_search_limit.help": "Maximale Entitäten, die von ha_search_entities zurückgegeben werden. Bereich 1-1000.",
376
374
  "advanced.automation_config_time_budget.label": "Automatisierungskonfigurations-Zeitbudget (s)",
377
375
  "advanced.automation_config_time_budget.help": "Maximale Zeit, die die Tiefensuche für das Abrufen von Automatisierungskonfigurationen aufwendet. Bereich 1-600. Neustart erforderlich.",
378
376
  "advanced.script_config_time_budget.label": "Skriptkonfigurations-Zeitbudget (s)",
@@ -371,8 +371,6 @@
371
371
  "advanced.verify_ssl.help": "Disable TLS verification only on trusted networks. Restart required.",
372
372
  "advanced.fuzzy_threshold.label": "Fuzzy-search threshold",
373
373
  "advanced.fuzzy_threshold.help": "Lower values allow looser entity matching. Range 0–100.",
374
- "advanced.entity_search_limit.label": "Entity search result limit",
375
- "advanced.entity_search_limit.help": "Maximum entities returned by ha_search_entities. Range 1–1000.",
376
374
  "advanced.automation_config_time_budget.label": "Automation config time budget (s)",
377
375
  "advanced.automation_config_time_budget.help": "Maximum time deep search spends fetching automation configs. Range 1–600. Restart required.",
378
376
  "advanced.script_config_time_budget.label": "Script config time budget (s)",
@@ -371,8 +371,6 @@
371
371
  "advanced.verify_ssl.help": "Désactive la vérification TLS uniquement sur les réseaux de confiance. Redémarrage requis.",
372
372
  "advanced.fuzzy_threshold.label": "Seuil de recherche floue",
373
373
  "advanced.fuzzy_threshold.help": "Des valeurs plus basses permettent une correspondance d'entités plus lâche. Plage 0–100.",
374
- "advanced.entity_search_limit.label": "Limite des résultats de recherche d'entités",
375
- "advanced.entity_search_limit.help": "Nombre maximum d'entités renvoyées par ha_search_entities. Plage 1–1000.",
376
374
  "advanced.automation_config_time_budget.label": "Budget de temps de configuration d'automatisation (s)",
377
375
  "advanced.automation_config_time_budget.help": "Temps maximum que la recherche approfondie consacre à la récupération des configurations d'automatisation. Plage 1–600. Redémarrage requis.",
378
376
  "advanced.script_config_time_budget.label": "Budget de temps de configuration de script (s)",
@@ -371,8 +371,6 @@
371
371
  "advanced.verify_ssl.help": "Отключайте проверку TLS только в доверенной сети. Нужен перезапуск.",
372
372
  "advanced.fuzzy_threshold.label": "Порог нечёткого поиска",
373
373
  "advanced.fuzzy_threshold.help": "Меньшее значение даёт более свободное сопоставление сущностей. Диапазон 0–100.",
374
- "advanced.entity_search_limit.label": "Лимит результатов поиска сущностей",
375
- "advanced.entity_search_limit.help": "Максимум сущностей, возвращаемых ha_search_entities. Диапазон 1–1000.",
376
374
  "advanced.automation_config_time_budget.label": "Бюджет времени конфигураций автоматизаций (с)",
377
375
  "advanced.automation_config_time_budget.help": "Максимальное время глубокого поиска для загрузки автоматизаций. 1–600. Нужен перезапуск.",
378
376
  "advanced.script_config_time_budget.label": "Бюджет времени конфигураций скриптов (с)",
@@ -371,8 +371,6 @@
371
371
  "advanced.verify_ssl.help": "仅在受信任网络上禁用 TLS 验证。需重启。",
372
372
  "advanced.fuzzy_threshold.label": "模糊搜索阈值",
373
373
  "advanced.fuzzy_threshold.help": "较低的值允许更宽松的实体匹配。范围 0–100。",
374
- "advanced.entity_search_limit.label": "实体搜索结果上限",
375
- "advanced.entity_search_limit.help": "ha_search_entities 返回的最大实体数。范围 1–1000。",
376
374
  "advanced.automation_config_time_budget.label": "自动化配置时间预算(秒)",
377
375
  "advanced.automation_config_time_budget.help": "深度搜索获取自动化配置所花费的最大时间。范围 1–600。需重启。",
378
376
  "advanced.script_config_time_budget.label": "脚本配置时间预算(秒)",