ha-mcp-dev 7.7.0.dev699__tar.gz → 7.8.0.dev702__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 (143) hide show
  1. {ha_mcp_dev-7.7.0.dev699/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.8.0.dev702}/PKG-INFO +2 -2
  2. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/pyproject.toml +2 -2
  3. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/settings.css +7 -0
  4. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/settings.js +71 -17
  5. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/settings_ui.py +19 -16
  6. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
  7. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp_dev.egg-info/requires.txt +1 -1
  8. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/LICENSE +0 -0
  9. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/MANIFEST.in +0 -0
  10. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/README.md +0 -0
  11. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/setup.cfg +0 -0
  12. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/__init__.py +0 -0
  13. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/__main__.py +0 -0
  14. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/_pypi_marker +0 -0
  15. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/_version.py +0 -0
  16. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/auth/__init__.py +0 -0
  17. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/auth/consent_form.py +0 -0
  18. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/auth/provider.py +0 -0
  19. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/backup_manager.py +0 -0
  20. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/client/__init__.py +0 -0
  21. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/client/rest_client.py +0 -0
  22. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/client/supervisor_client.py +0 -0
  23. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/client/websocket_client.py +0 -0
  24. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/client/websocket_listener.py +0 -0
  25. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/config.py +0 -0
  26. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
  27. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
  28. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
  29. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/errors.py +0 -0
  30. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/__init__.py +0 -0
  31. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/approval_queue.py +0 -0
  32. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/evaluator.py +0 -0
  33. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/handlers.py +0 -0
  34. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/middleware.py +0 -0
  35. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/model.py +0 -0
  36. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/persistence.py +0 -0
  37. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/policy/value_sources.py +0 -0
  38. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/py.typed +0 -0
  39. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/read_only.py +0 -0
  40. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
  41. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
  42. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
  43. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
  44. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
  45. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
  46. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
  47. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
  48. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
  49. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
  50. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
  51. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
  52. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
  53. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
  54. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
  55. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
  56. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
  57. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
  58. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
  59. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
  60. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
  61. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
  62. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/server.py +0 -0
  63. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/smoke_test.py +0 -0
  64. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
  65. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/__init__.py +0 -0
  66. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/auto_backup.py +0 -0
  67. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/backup.py +0 -0
  68. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/best_practice_checker.py +0 -0
  69. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/config_entry_flow.py +0 -0
  70. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/device_control.py +0 -0
  71. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/enhanced.py +0 -0
  72. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/helpers.py +0 -0
  73. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/reference_validator.py +0 -0
  74. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/registry.py +0 -0
  75. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
  76. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_base.py +0 -0
  77. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_config.py +0 -0
  78. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
  79. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
  80. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
  81. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
  82. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
  83. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
  84. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
  85. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_addons.py +0 -0
  86. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_areas.py +0 -0
  87. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_blueprints.py +0 -0
  88. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_bug_report.py +0 -0
  89. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_calendar.py +0 -0
  90. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_camera.py +0 -0
  91. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_categories.py +0 -0
  92. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_code.py +0 -0
  93. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_config_automations.py +0 -0
  94. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
  95. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
  96. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
  97. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
  98. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
  99. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_energy.py +0 -0
  100. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_entities.py +0 -0
  101. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_filesystem.py +0 -0
  102. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_groups.py +0 -0
  103. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_hacs.py +0 -0
  104. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_history.py +0 -0
  105. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_integrations.py +0 -0
  106. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_labels.py +0 -0
  107. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
  108. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_registry.py +0 -0
  109. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_resources.py +0 -0
  110. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_search.py +0 -0
  111. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_service.py +0 -0
  112. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_services.py +0 -0
  113. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_system.py +0 -0
  114. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_themes.py +0 -0
  115. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_todo.py +0 -0
  116. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_traces.py +0 -0
  117. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_updates.py +0 -0
  118. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_utility.py +0 -0
  119. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
  120. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
  121. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/tools_zones.py +0 -0
  122. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/util_helpers.py +0 -0
  123. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/tools/validation_middleware.py +0 -0
  124. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/transforms/__init__.py +0 -0
  125. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/transforms/categorized_search.py +0 -0
  126. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
  127. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/__init__.py +0 -0
  128. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/config_hash.py +0 -0
  129. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/data_paths.py +0 -0
  130. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/domain_handlers.py +0 -0
  131. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/fuzzy_search.py +0 -0
  132. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
  133. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/operation_manager.py +0 -0
  134. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/python_sandbox.py +0 -0
  135. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/skill_loader.py +0 -0
  136. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp/utils/usage_logger.py +0 -0
  137. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
  138. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
  139. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
  140. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
  141. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/tests/__init__.py +0 -0
  142. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/tests/test_constants.py +0 -0
  143. {ha_mcp_dev-7.7.0.dev699 → ha_mcp_dev-7.8.0.dev702}/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.7.0.dev699
3
+ Version: 7.8.0.dev702
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
@@ -24,7 +24,7 @@ Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
25
25
  Requires-Dist: truststore==0.10.4
26
26
  Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==48.0.0
27
+ Requires-Dist: cryptography==49.0.0
28
28
  Requires-Dist: pydantic-monty==0.0.18
29
29
  Dynamic: license-file
30
30
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ha-mcp-dev"
7
- version = "7.7.0.dev699"
7
+ version = "7.8.0.dev702"
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"
@@ -30,7 +30,7 @@ dependencies = [
30
30
  "python-dotenv==1.2.2",
31
31
  "truststore==0.10.4",
32
32
  "websockets==16.0",
33
- "cryptography==48.0.0",
33
+ "cryptography==49.0.0",
34
34
  "pydantic-monty==0.0.18",
35
35
  ]
36
36
 
@@ -415,6 +415,13 @@
415
415
  fieldset.a11y-options { border: 0; padding: 0; margin: 0; min-inline-size: auto; }
416
416
  .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0;
417
417
  margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
418
+ /* Skip-to-content link: visually hidden until focused, then pinned to the
419
+ top-left so keyboard users can bypass the header. */
420
+ .skip-link { position: absolute; left: 8px; top: -56px; z-index: 100;
421
+ padding: 8px 16px; border-radius: 8px; font-size: 0.875rem; font-weight: 600;
422
+ background: var(--surface); color: var(--text); border: 1px solid var(--border);
423
+ transition: top 0.15s ease; }
424
+ .skip-link:focus { top: 8px; outline: 2px solid var(--accent); outline-offset: 2px; }
418
425
  .a11y-option { display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
419
426
  border: 1px solid var(--border); border-radius: 8px; cursor: pointer; font-size: 0.85rem;
420
427
  background: var(--bg); user-select: none; }
@@ -9,11 +9,13 @@ window.addEventListener('error', (e) => {
9
9
  const el = document.getElementById('status');
10
10
  if (!el) return;
11
11
  const where = e.filename ? `${e.filename}:${e.lineno}:${e.colno}` : 'inline';
12
+ setStatusAlert(el, true);
12
13
  el.textContent = `JS error: ${e.message} @ ${where}`;
13
14
  });
14
15
  window.addEventListener('unhandledrejection', (e) => {
15
16
  const el = document.getElementById('status');
16
17
  if (!el) return;
18
+ setStatusAlert(el, true);
17
19
  el.textContent = `Async error: ${e.reason && e.reason.message ? e.reason.message : String(e.reason)}`;
18
20
  });
19
21
 
@@ -135,18 +137,18 @@ async function loadTools() {
135
137
  try {
136
138
  resp = await fetch('./api/settings/tools');
137
139
  } catch (e) {
138
- updateStatus('Network error reaching /api/settings/tools: ' + e.message);
140
+ updateStatus('Network error reaching /api/settings/tools: ' + e.message, false, true);
139
141
  return;
140
142
  }
141
143
  if (!resp.ok) {
142
- updateStatus(`/api/settings/tools returned HTTP ${resp.status} ${resp.statusText}`);
144
+ updateStatus(`/api/settings/tools returned HTTP ${resp.status} ${resp.statusText}`, false, true);
143
145
  return;
144
146
  }
145
147
  let data;
146
148
  try {
147
149
  data = await resp.json();
148
150
  } catch (e) {
149
- updateStatus('Failed to parse /api/settings/tools response as JSON: ' + e.message);
151
+ updateStatus('Failed to parse /api/settings/tools response as JSON: ' + e.message, false, true);
150
152
  return;
151
153
  }
152
154
  toolData = data.tools || [];
@@ -169,14 +171,15 @@ async function loadTools() {
169
171
  // the user where to look instead of leaving them on "Loading".
170
172
  updateStatus(
171
173
  'No tools found. The sidecar reads ~/.ha-mcp/tool_metadata.json — ' +
172
- 'if missing/empty, restart your MCP client. See ~/.ha-mcp/sidecar.log for details.'
174
+ 'if missing/empty, restart your MCP client. See ~/.ha-mcp/sidecar.log for details.',
175
+ false, true
173
176
  );
174
177
  return;
175
178
  }
176
179
  try {
177
180
  render();
178
181
  } catch (e) {
179
- updateStatus('Render failed: ' + e.message + ' (open browser devtools for the stack)');
182
+ updateStatus('Render failed: ' + e.message + ' (open browser devtools for the stack)', false, true);
180
183
  throw e;
181
184
  }
182
185
  updateStatus('Loaded');
@@ -765,14 +768,24 @@ async function saveConfig() {
765
768
  // are on.
766
769
  if (restartChannel) restartChannel.postMessage({type: 'restart-required'});
767
770
  } else {
768
- updateStatus('Save failed!');
771
+ updateStatus('Save failed!', false, true);
769
772
  }
770
773
  }
771
774
 
772
- function updateStatus(text, saved) {
775
+ // Reflect success/error semantics on a status span for assistive tech:
776
+ // failures switch to role=alert/assertive so screen readers interrupt; all
777
+ // other updates stay role=status/polite (matching the static markup). (#1596)
778
+ function setStatusAlert(el, isError) {
779
+ if (!el) return;
780
+ el.setAttribute('role', isError ? 'alert' : 'status');
781
+ el.setAttribute('aria-live', isError ? 'assertive' : 'polite');
782
+ }
783
+
784
+ function updateStatus(text, saved, isError) {
773
785
  const el = document.getElementById('status');
774
- el.textContent = text;
786
+ setStatusAlert(el, isError);
775
787
  el.className = saved ? 'status saved' : 'status';
788
+ el.textContent = text;
776
789
  }
777
790
 
778
791
  function applyToolSearch() {
@@ -916,6 +929,7 @@ async function saveBackupConfig() {
916
929
  return;
917
930
  }
918
931
  btn.disabled = true;
932
+ setStatusAlert(statusEl, false);
919
933
  statusEl.textContent = 'Saving…';
920
934
  try {
921
935
  const resp = await fetch('./api/settings/backup-config', {
@@ -931,6 +945,7 @@ async function saveBackupConfig() {
931
945
  if (typeof data.error === 'string') msg = data.error;
932
946
  else if (data.error.message) msg = data.error.message;
933
947
  }
948
+ setStatusAlert(statusEl, true);
934
949
  statusEl.textContent = msg;
935
950
  return;
936
951
  }
@@ -958,6 +973,7 @@ async function saveBackupConfig() {
958
973
  }
959
974
  } catch (err) {
960
975
  btn.disabled = false;
976
+ setStatusAlert(statusEl, true);
961
977
  statusEl.textContent = 'Network error: ' + String(err);
962
978
  }
963
979
  }
@@ -1050,6 +1066,8 @@ function renderFsCustomPathsSubForm(parentEl, masterOn, fsOn) {
1050
1066
  const status = document.createElement('div');
1051
1067
  status.id = 'fsCustomPathsStatus';
1052
1068
  status.className = 'feature-help';
1069
+ status.setAttribute('role', 'status');
1070
+ status.setAttribute('aria-live', 'polite');
1053
1071
  control.appendChild(ta);
1054
1072
  control.appendChild(btn);
1055
1073
  control.appendChild(status);
@@ -1070,6 +1088,7 @@ async function saveFsCustomPaths() {
1070
1088
  .map(s => s.trim())
1071
1089
  .filter(s => s.length);
1072
1090
  btn.disabled = true;
1091
+ setStatusAlert(statusEl, false);
1073
1092
  statusEl.textContent = 'Saving…';
1074
1093
  try {
1075
1094
  const resp = await fetch('./api/settings/fs-custom-paths', {
@@ -1085,6 +1104,7 @@ async function saveFsCustomPaths() {
1085
1104
  if (typeof data.error === 'string') msg = data.error;
1086
1105
  else if (data.error.message) msg = data.error.message;
1087
1106
  }
1107
+ setStatusAlert(statusEl, true);
1088
1108
  statusEl.textContent = msg;
1089
1109
  return;
1090
1110
  }
@@ -1101,6 +1121,7 @@ async function saveFsCustomPaths() {
1101
1121
  : 'Saved.';
1102
1122
  } catch (err) {
1103
1123
  btn.disabled = false;
1124
+ setStatusAlert(statusEl, true);
1104
1125
  statusEl.textContent = 'Network error: ' + String(err);
1105
1126
  }
1106
1127
  }
@@ -2453,15 +2474,18 @@ async function removePolicyRule(toolName) {
2453
2474
 
2454
2475
  async function saveGlobalSettings() {
2455
2476
  const statusEl = document.getElementById('policy-global-save-status');
2477
+ setStatusAlert(statusEl, false);
2456
2478
  statusEl.textContent = 'Saving...';
2457
2479
  let resp;
2458
2480
  try {
2459
2481
  resp = await fetch('./api/policy/config');
2460
2482
  } catch (e) {
2483
+ setStatusAlert(statusEl, true);
2461
2484
  statusEl.textContent = 'Network error: ' + e.message;
2462
2485
  return;
2463
2486
  }
2464
2487
  if (!resp.ok) {
2488
+ setStatusAlert(statusEl, true);
2465
2489
  statusEl.textContent = 'Load failed: ' + resp.status;
2466
2490
  return;
2467
2491
  }
@@ -2472,6 +2496,7 @@ async function saveGlobalSettings() {
2472
2496
  await policyPut(policy, 'Save global settings');
2473
2497
  statusEl.textContent = 'Saved.';
2474
2498
  } catch (e) {
2499
+ setStatusAlert(statusEl, true);
2475
2500
  statusEl.textContent = e.message;
2476
2501
  }
2477
2502
  }
@@ -2629,10 +2654,18 @@ setInterval(() => {
2629
2654
  // Generic dispatcher — every .tab button names its target panel via
2630
2655
  // data-panel, every .panel has matching id="panel-<name>". Adding a
2631
2656
  // new tab is one button + one panel div; no JS change needed.
2632
- function activateTab(target) {
2633
- document.querySelectorAll('.tab').forEach(t =>
2634
- t.classList.toggle('active', t.dataset.panel === target)
2635
- );
2657
+ function activateTab(target, opts) {
2658
+ const focusTab = opts && opts.focusTab;
2659
+ document.querySelectorAll('.tab').forEach(t => {
2660
+ const selected = t.dataset.panel === target;
2661
+ t.classList.toggle('active', selected);
2662
+ // Expose tab state + roving tabindex to assistive tech (WAI-ARIA APG
2663
+ // tabs pattern). Only the selected tab stays in the Tab sequence;
2664
+ // arrow keys move between the rest. (#1596)
2665
+ t.setAttribute('aria-selected', selected ? 'true' : 'false');
2666
+ t.tabIndex = selected ? 0 : -1;
2667
+ if (selected && focusTab) t.focus();
2668
+ });
2636
2669
  document.querySelectorAll('.panel').forEach(p =>
2637
2670
  p.classList.toggle('active', p.id === 'panel-' + target)
2638
2671
  );
@@ -2649,6 +2682,27 @@ document.querySelectorAll('.tab').forEach(tab => {
2649
2682
  tab.addEventListener('click', () => activateTab(tab.dataset.panel));
2650
2683
  });
2651
2684
 
2685
+ // Keyboard navigation for the tablist (WAI-ARIA APG tabs pattern): Left/Right
2686
+ // move + activate the adjacent tab, Home/End jump to the ends. (#1596)
2687
+ {
2688
+ const tablist = document.querySelector('.tabs[role="tablist"]');
2689
+ if (tablist) {
2690
+ tablist.addEventListener('keydown', (e) => {
2691
+ const tabs = Array.from(tablist.querySelectorAll('.tab'));
2692
+ const currentIndex = tabs.indexOf(document.activeElement);
2693
+ if (currentIndex === -1) return;
2694
+ let nextIndex = null;
2695
+ if (e.key === 'ArrowRight') nextIndex = (currentIndex + 1) % tabs.length;
2696
+ else if (e.key === 'ArrowLeft') nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
2697
+ else if (e.key === 'Home') nextIndex = 0;
2698
+ else if (e.key === 'End') nextIndex = tabs.length - 1;
2699
+ if (nextIndex === null) return;
2700
+ e.preventDefault();
2701
+ activateTab(tabs[nextIndex].dataset.panel, { focusTab: true });
2702
+ });
2703
+ }
2704
+ }
2705
+
2652
2706
  // Cross-tab links — any <a data-panel-link="<name>"> switches tabs
2653
2707
  // in-page rather than following the href (used by the "no gated
2654
2708
  // tools" empty state to point users at the Tools tab).
@@ -2858,8 +2912,8 @@ function _advSaveStatusEls() {
2858
2912
  document.getElementById('advSaveStatusTop'),
2859
2913
  ].filter(Boolean);
2860
2914
  }
2861
- function _setAdvSaveStatus(text) {
2862
- _advSaveStatusEls().forEach(el => { el.textContent = text; });
2915
+ function _setAdvSaveStatus(text, isError) {
2916
+ _advSaveStatusEls().forEach(el => { setStatusAlert(el, isError); el.textContent = text; });
2863
2917
  }
2864
2918
  function _setAdvSaveDisabled(disabled) {
2865
2919
  _advSaveBtns().forEach(b => { b.disabled = disabled; });
@@ -2934,7 +2988,7 @@ async function saveAdvancedSettings() {
2934
2988
  data = {restart_required: true};
2935
2989
  } else {
2936
2990
  _setAdvSaveDisabled(false);
2937
- _setAdvSaveStatus(`Save failed (HTTP ${resp.status}, non-JSON body)`);
2991
+ _setAdvSaveStatus(`Save failed (HTTP ${resp.status}, non-JSON body)`, true);
2938
2992
  return;
2939
2993
  }
2940
2994
  }
@@ -2945,7 +2999,7 @@ async function saveAdvancedSettings() {
2945
2999
  if (typeof data.error === 'string') msg = data.error;
2946
3000
  else if (data.error.message) msg = data.error.message;
2947
3001
  }
2948
- _setAdvSaveStatus(msg);
3002
+ _setAdvSaveStatus(msg, true);
2949
3003
  return;
2950
3004
  }
2951
3005
  }
@@ -2973,7 +3027,7 @@ async function saveAdvancedSettings() {
2973
3027
  }
2974
3028
  } catch (err) {
2975
3029
  _setAdvSaveDisabled(false);
2976
- _setAdvSaveStatus('Network error: ' + String(err));
3030
+ _setAdvSaveStatus('Network error: ' + String(err), true);
2977
3031
  }
2978
3032
  }
2979
3033
 
@@ -787,6 +787,7 @@ _SETTINGS_HTML = (
787
787
  + """</style>
788
788
  </head>
789
789
  <body>
790
+ <a href="#main-content" class="skip-link">Skip to content</a>
790
791
  <div class="header">
791
792
  <h1>HA-MCP Settings</h1>
792
793
  <div style="display:flex;align-items:center;gap:8px">
@@ -798,15 +799,16 @@ _SETTINGS_HTML = (
798
799
  <option value="dark">Dark</option>
799
800
  </select>
800
801
  </label>
801
- <span id="status" class="status">Loading...</span>
802
+ <span id="status" class="status" role="status" aria-live="polite">Loading...</span>
802
803
  </div>
803
804
  </div>
804
- <div class="tabs">
805
- <button class="tab active" data-panel="tools">Tools</button>
806
- <button class="tab" data-panel="server">Server Settings</button>
807
- <button class="tab" data-panel="backups">Backups</button>
808
- <button class="tab" data-panel="tool-security-policies">Tool Security Policies</button>
809
- <button class="tab" data-panel="accessibility">Accessibility</button>
805
+ <main id="main-content" tabindex="-1" style="outline:none">
806
+ <div class="tabs" role="tablist" aria-label="Settings sections">
807
+ <button class="tab active" data-panel="tools" role="tab" id="tab-tools" aria-controls="panel-tools" aria-selected="true">Tools</button>
808
+ <button class="tab" data-panel="server" role="tab" id="tab-server" aria-controls="panel-server" aria-selected="false" tabindex="-1">Server Settings</button>
809
+ <button class="tab" data-panel="backups" role="tab" id="tab-backups" aria-controls="panel-backups" aria-selected="false" tabindex="-1">Backups</button>
810
+ <button class="tab" data-panel="tool-security-policies" role="tab" id="tab-tool-security-policies" aria-controls="panel-tool-security-policies" aria-selected="false" tabindex="-1">Tool Security Policies</button>
811
+ <button class="tab" data-panel="accessibility" role="tab" id="tab-accessibility" aria-controls="panel-accessibility" aria-selected="false" tabindex="-1">Accessibility</button>
810
812
  </div>
811
813
  <div class="restart-notice" id="restartNotice">
812
814
  <span class="restart-notice-text" id="restartNoticeText">
@@ -819,7 +821,7 @@ _SETTINGS_HTML = (
819
821
  </span>
820
822
  <button class="restart-btn" id="restartBtn" style="display:none">Restart Add-on</button>
821
823
  </div>
822
- <div class="panel active" id="panel-tools">
824
+ <div class="panel active" id="panel-tools" role="tabpanel" aria-labelledby="tab-tools" tabindex="0">
823
825
  <div class="readonly-notice">
824
826
  Server-wide features (Tool Search, YAML config editing, filesystem
825
827
  tools, etc.) appear in both the <strong>Server Settings</strong>
@@ -859,7 +861,7 @@ _SETTINGS_HTML = (
859
861
  <input type="text" class="search" id="search" placeholder="Search tools...">
860
862
  <div id="groups"></div>
861
863
  </div>
862
- <div class="panel" id="panel-server">
864
+ <div class="panel" id="panel-server" role="tabpanel" aria-labelledby="tab-server" tabindex="0">
863
865
  <div class="features-sub">
864
866
  Tool Search, advanced settings. Changes take effect only after you
865
867
  restart the add-on (applies the change server-side) AND reconnect or
@@ -880,7 +882,7 @@ _SETTINGS_HTML = (
880
882
  </div>
881
883
  <div id="advSaveRowTop" class="adv-save-row" style="display:none;">
882
884
  <button id="advSaveBtnTop" class="adv-save-btn">💾 Save advanced settings</button>
883
- <span id="advSaveStatusTop" class="status"></span>
885
+ <span id="advSaveStatusTop" class="status" role="status" aria-live="polite"></span>
884
886
  </div>
885
887
  <div id="featuresBody"></div>
886
888
 
@@ -917,7 +919,7 @@ _SETTINGS_HTML = (
917
919
  </div>
918
920
  <div id="advSaveRow" class="adv-save-row" style="display:none;">
919
921
  <button id="advSaveBtn" class="adv-save-btn">💾 Save advanced settings</button>
920
- <span id="advSaveStatus" class="status"></span>
922
+ <span id="advSaveStatus" class="status" role="status" aria-live="polite"></span>
921
923
  </div>
922
924
 
923
925
  <div id="sidecarStopRow" style="display:none; margin: 16px 0; text-align: right;">
@@ -926,13 +928,13 @@ _SETTINGS_HTML = (
926
928
  >Permanently disable settings server</button>
927
929
  </div>
928
930
  </div>
929
- <div class="panel" id="panel-backups">
931
+ <div class="panel" id="panel-backups" role="tabpanel" aria-labelledby="tab-backups" tabindex="0">
930
932
  <div class="backup-state" id="backupState">Loading backup state…</div>
931
933
  <div class="backup-config" id="backupConfig">
932
934
  <div class="backup-config-form" id="backupConfigForm"></div>
933
935
  <div class="backup-config-actions" id="backupConfigActions" style="display:none">
934
936
  <button id="backupConfigSave">Save settings</button>
935
- <span id="backupConfigStatus" class="status"></span>
937
+ <span id="backupConfigStatus" class="status" role="status" aria-live="polite"></span>
936
938
  </div>
937
939
  </div>
938
940
  <div class="backup-filters">
@@ -943,7 +945,7 @@ _SETTINGS_HTML = (
943
945
  </div>
944
946
  <div id="backupList"></div>
945
947
  </div>
946
- <div class="panel" id="panel-tool-security-policies">
948
+ <div class="panel" id="panel-tool-security-policies" role="tabpanel" aria-labelledby="tab-tool-security-policies" tabindex="0">
947
949
  <h2>Tool Security Policies</h2>
948
950
  <p class="features-sub">
949
951
  Per-tool approval gating for high-stakes calls. Use the
@@ -991,7 +993,7 @@ _SETTINGS_HTML = (
991
993
  </div>
992
994
  <div style="margin-top:10px; display:flex; align-items:center; gap:12px">
993
995
  <button id="policy-save-global-btn" class="restart-btn">Save global settings</button>
994
- <span id="policy-global-save-status" class="status"></span>
996
+ <span id="policy-global-save-status" class="status" role="status" aria-live="polite"></span>
995
997
  </div>
996
998
  </section>
997
999
 
@@ -1010,7 +1012,7 @@ _SETTINGS_HTML = (
1010
1012
  <div id="policy-rules-list"></div>
1011
1013
  </section>
1012
1014
  </div>
1013
- <div class="panel" id="panel-accessibility">
1015
+ <div class="panel" id="panel-accessibility" role="tabpanel" aria-labelledby="tab-accessibility" tabindex="0">
1014
1016
  <p class="tool-desc" style="margin-bottom:16px">
1015
1017
  These settings apply immediately and are saved in this browser and on the
1016
1018
  server, so they survive restarts in every mode (including stdio, where the
@@ -1064,6 +1066,7 @@ _SETTINGS_HTML = (
1064
1066
  <button id="a11y-reset" class="restart-btn" type="button">Reset to defaults</button>
1065
1067
  </section>
1066
1068
  </div>
1069
+ </main>
1067
1070
  <div class="modal-backdrop" id="modalBackdrop">
1068
1071
  <div class="modal">
1069
1072
  <div class="modal-header">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ha-mcp-dev
3
- Version: 7.7.0.dev699
3
+ Version: 7.8.0.dev702
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
@@ -24,7 +24,7 @@ Requires-Dist: pydantic==2.13.4
24
24
  Requires-Dist: python-dotenv==1.2.2
25
25
  Requires-Dist: truststore==0.10.4
26
26
  Requires-Dist: websockets==16.0
27
- Requires-Dist: cryptography==48.0.0
27
+ Requires-Dist: cryptography==49.0.0
28
28
  Requires-Dist: pydantic-monty==0.0.18
29
29
  Dynamic: license-file
30
30
 
@@ -4,5 +4,5 @@ pydantic==2.13.4
4
4
  python-dotenv==1.2.2
5
5
  truststore==0.10.4
6
6
  websockets==16.0
7
- cryptography==48.0.0
7
+ cryptography==49.0.0
8
8
  pydantic-monty==0.0.18