ha-mcp-dev 7.7.0.dev698__tar.gz → 7.7.0.dev699__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.
- {ha_mcp_dev-7.7.0.dev698/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.7.0.dev699}/PKG-INFO +1 -1
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/pyproject.toml +1 -1
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/__main__.py +37 -7
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/rest_client.py +56 -31
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_hacs.py +8 -6
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/LICENSE +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/README.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/setup.cfg +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings.css +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings.js +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/tests/test_env_manager.py +0 -0
|
@@ -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.
|
|
7
|
+
version = "7.7.0.dev699"
|
|
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"
|
|
@@ -290,17 +290,28 @@ def _setup_standard_mode() -> None:
|
|
|
290
290
|
_log_startup_version()
|
|
291
291
|
|
|
292
292
|
|
|
293
|
-
def _http_run_kwargs(transport: str, host: str, port: int, path: str) -> dict:
|
|
294
|
-
"""Build common run_async kwargs for HTTP-based transports.
|
|
295
|
-
|
|
293
|
+
def _http_run_kwargs(transport: str, host: str, port: int, path: str) -> dict[str, Any]:
|
|
294
|
+
"""Build common run_async kwargs for HTTP-based transports.
|
|
295
|
+
|
|
296
|
+
``stateless_http`` is a Streamable-HTTP concept and is only valid for the
|
|
297
|
+
``http``/``streamable-http`` transports. Passing it alongside
|
|
298
|
+
``transport="sse"`` makes fastmcp's ``run_async`` raise
|
|
299
|
+
``ValueError("SSE transport does not support stateless mode")``. Gating it to
|
|
300
|
+
non-SSE transports keeps SSE startup working. (Before this fix that raise was
|
|
301
|
+
also swallowed into a silent exit 0; ``_run_with_shutdown`` now surfaces a
|
|
302
|
+
self-terminating server task's exception instead.) See #1544.
|
|
303
|
+
"""
|
|
304
|
+
kwargs: dict[str, Any] = {
|
|
296
305
|
"transport": transport,
|
|
297
306
|
"host": host,
|
|
298
307
|
"port": port,
|
|
299
308
|
"path": path,
|
|
300
309
|
"show_banner": _get_show_banner(),
|
|
301
|
-
"stateless_http": True,
|
|
302
310
|
"uvicorn_config": {"log_config": _get_timestamped_uvicorn_log_config()},
|
|
303
311
|
}
|
|
312
|
+
if transport != "sse":
|
|
313
|
+
kwargs["stateless_http"] = True
|
|
314
|
+
return kwargs
|
|
304
315
|
|
|
305
316
|
|
|
306
317
|
def _create_server() -> "HomeAssistantSmartMCPServer":
|
|
@@ -572,9 +583,23 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
|
|
|
572
583
|
# Expected: we just cancelled server_task above; swallow its
|
|
573
584
|
# CancelledError so shutdown can proceed to cleanup.
|
|
574
585
|
pass
|
|
586
|
+
elif server_task in done:
|
|
587
|
+
# Server task finished on its own (no shutdown signal). Re-raise any
|
|
588
|
+
# exception it captured so a hard startup failure surfaces as a
|
|
589
|
+
# logged sys.exit(1) instead of a silent exit 0 — without this the
|
|
590
|
+
# exception on the already-done task is never retrieved. See #1544.
|
|
591
|
+
server_task.result()
|
|
575
592
|
|
|
576
593
|
except asyncio.CancelledError:
|
|
577
|
-
|
|
594
|
+
# A shutdown-initiated cancel is a graceful stop. A cancel without a
|
|
595
|
+
# shutdown signal — including one re-raised by server_task.result()
|
|
596
|
+
# above — is a hard stop masquerading as success; re-raise it so it
|
|
597
|
+
# becomes a logged sys.exit(1) rather than a silent exit 0. See #1544.
|
|
598
|
+
if _shutdown_event is not None and _shutdown_event.is_set():
|
|
599
|
+
logger.info("Server task cancelled")
|
|
600
|
+
else:
|
|
601
|
+
logger.error("Server task cancelled without a shutdown signal")
|
|
602
|
+
raise
|
|
578
603
|
finally:
|
|
579
604
|
try:
|
|
580
605
|
await asyncio.wait_for(
|
|
@@ -583,7 +608,12 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
|
|
|
583
608
|
except TimeoutError:
|
|
584
609
|
logger.warning("Resource cleanup timed out")
|
|
585
610
|
|
|
586
|
-
|
|
611
|
+
try:
|
|
612
|
+
await _cancel_tasks(server_task, shutdown_task)
|
|
613
|
+
except Exception as e:
|
|
614
|
+
# Teardown must never mask the exception being propagated from the
|
|
615
|
+
# try block (Python drops the original if finally raises).
|
|
616
|
+
logger.warning(f"Task cancellation during shutdown failed: {e}")
|
|
587
617
|
|
|
588
618
|
|
|
589
619
|
def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
|
|
@@ -597,7 +627,7 @@ def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
|
|
|
597
627
|
except SystemExit:
|
|
598
628
|
raise
|
|
599
629
|
except Exception as e:
|
|
600
|
-
logger.error(f"{label} error: {e}")
|
|
630
|
+
logger.error(f"{label} error: {e}", exc_info=True)
|
|
601
631
|
sys.exit(1)
|
|
602
632
|
|
|
603
633
|
sys.exit(0)
|
|
@@ -33,6 +33,12 @@ def _is_ssl_error(exc: BaseException) -> bool:
|
|
|
33
33
|
|
|
34
34
|
logger = logging.getLogger(__name__)
|
|
35
35
|
|
|
36
|
+
# Transient gateway statuses from a reverse proxy / Supervisor ingress — HA Core
|
|
37
|
+
# restarting or briefly overloaded behind it. The upstream couldn't be reached,
|
|
38
|
+
# so the request did not execute and retrying is safe even for writes.
|
|
39
|
+
_RETRYABLE_STATUS = frozenset({502, 503, 504})
|
|
40
|
+
_MAX_REQUEST_ATTEMPTS = 3
|
|
41
|
+
|
|
36
42
|
|
|
37
43
|
class HomeAssistantError(Exception):
|
|
38
44
|
"""Base exception for Home Assistant API errors."""
|
|
@@ -180,7 +186,8 @@ class HomeAssistantClient:
|
|
|
180
186
|
|
|
181
187
|
Handles auth, HTTP 4xx/5xx, and transport errors in one place.
|
|
182
188
|
Callers parse the body themselves (JSON via `_request`, text via
|
|
183
|
-
`get_addon_logs`, etc.).
|
|
189
|
+
`get_addon_logs`, etc.). Transient gateway errors (502/503/504) are
|
|
190
|
+
retried with bounded exponential backoff before surfacing.
|
|
184
191
|
|
|
185
192
|
Raises:
|
|
186
193
|
HomeAssistantAuthError: 401 response.
|
|
@@ -188,44 +195,62 @@ class HomeAssistantClient:
|
|
|
188
195
|
response_data set from JSON body when possible).
|
|
189
196
|
HomeAssistantConnectionError: Network, timeout, or transport error.
|
|
190
197
|
"""
|
|
191
|
-
|
|
192
|
-
|
|
198
|
+
backoff = 0.5
|
|
199
|
+
for attempt in range(1, _MAX_REQUEST_ATTEMPTS + 1):
|
|
200
|
+
try:
|
|
201
|
+
response = await self.httpx_client.request(method, endpoint, **kwargs)
|
|
193
202
|
|
|
194
|
-
|
|
195
|
-
|
|
203
|
+
if response.status_code == 401:
|
|
204
|
+
raise HomeAssistantAuthError("Invalid authentication token")
|
|
196
205
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
206
|
+
if response.status_code >= 400:
|
|
207
|
+
try:
|
|
208
|
+
error_data = response.json()
|
|
209
|
+
except Exception:
|
|
210
|
+
error_data = {"message": response.text}
|
|
202
211
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
message = error_data.get("message")
|
|
213
|
+
if not message or not message.strip():
|
|
214
|
+
message = response.reason_phrase or "<empty body>"
|
|
206
215
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
if (
|
|
217
|
+
response.status_code in _RETRYABLE_STATUS
|
|
218
|
+
and attempt < _MAX_REQUEST_ATTEMPTS
|
|
219
|
+
):
|
|
220
|
+
logger.warning(
|
|
221
|
+
f"Transient {response.status_code} from Home Assistant "
|
|
222
|
+
f"(attempt {attempt}/{_MAX_REQUEST_ATTEMPTS}), retrying "
|
|
223
|
+
f"in {backoff}s: {message}"
|
|
224
|
+
)
|
|
225
|
+
await asyncio.sleep(backoff)
|
|
226
|
+
backoff *= 2
|
|
227
|
+
continue
|
|
212
228
|
|
|
213
|
-
|
|
229
|
+
raise HomeAssistantAPIError(
|
|
230
|
+
f"API error: {response.status_code} - {message}",
|
|
231
|
+
status_code=response.status_code,
|
|
232
|
+
response_data=error_data,
|
|
233
|
+
)
|
|
214
234
|
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
return response
|
|
236
|
+
|
|
237
|
+
except httpx.ConnectError as e:
|
|
238
|
+
if _is_ssl_error(e) and self.verify_ssl:
|
|
239
|
+
raise HomeAssistantConnectionError(
|
|
240
|
+
f"TLS verification failed for {self.base_url}: {e}. "
|
|
241
|
+
"If this is a self-signed certificate or hostname "
|
|
242
|
+
"mismatch, set HA_VERIFY_SSL=false to skip verification."
|
|
243
|
+
) from e
|
|
217
244
|
raise HomeAssistantConnectionError(
|
|
218
|
-
f"
|
|
219
|
-
"If this is a self-signed certificate or hostname "
|
|
220
|
-
"mismatch, set HA_VERIFY_SSL=false to skip verification."
|
|
245
|
+
f"Failed to connect to Home Assistant: {e}"
|
|
221
246
|
) from e
|
|
222
|
-
|
|
223
|
-
f"
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
247
|
+
except httpx.TimeoutException as e:
|
|
248
|
+
raise HomeAssistantConnectionError(f"Request timeout: {e}") from e
|
|
249
|
+
except httpx.HTTPError as e:
|
|
250
|
+
raise HomeAssistantConnectionError(f"HTTP error: {e}") from e
|
|
251
|
+
|
|
252
|
+
# Unreachable: the final attempt takes the non-retry branch and raises.
|
|
253
|
+
raise AssertionError("_raw_request retry loop exhausted without returning")
|
|
229
254
|
|
|
230
255
|
async def _request(
|
|
231
256
|
self, method: str, endpoint: str, **kwargs: Any
|
|
@@ -674,12 +674,14 @@ def _filter_and_score_repos(
|
|
|
674
674
|
HACS_REPOSITORY_SIGNAL = "hacs_dispatch_repository"
|
|
675
675
|
|
|
676
676
|
# Wall-clock budget for ``wait_for_repo_registration``. Generous
|
|
677
|
-
# because the constraint is "HACS finishes registration"
|
|
678
|
-
#
|
|
679
|
-
#
|
|
680
|
-
#
|
|
681
|
-
#
|
|
682
|
-
|
|
677
|
+
# because the constraint is "HACS finishes registration": adding a
|
|
678
|
+
# fresh repo makes HACS clone/index it over the network, which on a
|
|
679
|
+
# slow link (or a loaded HAOS E2E runner) can exceed 30 s — the prior
|
|
680
|
+
# value, which flaked ``test_install_mcp_tools_*`` with "Could not
|
|
681
|
+
# find repository ID after adding". The subscription nudges us the
|
|
682
|
+
# instant registration lands, so the happy path returns in seconds and
|
|
683
|
+
# this larger cap only ever costs wall-clock on a genuinely slow add.
|
|
684
|
+
HACS_REPO_REGISTRATION_TIMEOUT = 60.0
|
|
683
685
|
|
|
684
686
|
# Budget for the initial ``hacs/subscribe`` ack. Smaller than the
|
|
685
687
|
# overall registration timeout so a slow subscribe doesn't consume
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/provision.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/AGENTS.md
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/CLAUDE.md
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/best_practice_checker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_entities.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_overview.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scenes.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scoring.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tool_search_hint_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_scripts.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_dashboard_screenshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_voice_assistant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/validation_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/lite_docstrings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/kill_signal_diagnostics.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev698 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|