ha-mcp-dev 7.7.0.dev697__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.dev697/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.7.0.dev699}/PKG-INFO +1 -1
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/pyproject.toml +1 -1
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/__main__.py +106 -9
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/rest_client.py +56 -31
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_hacs.py +8 -6
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/LICENSE +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/README.md +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/setup.cfg +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.7.0.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.7.0.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings.css +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings.js +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/settings_ui.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.7.0.dev697 → 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":
|
|
@@ -402,6 +413,46 @@ class ToolValidationLogFilter(logging.Filter):
|
|
|
402
413
|
return True
|
|
403
414
|
|
|
404
415
|
|
|
416
|
+
class ProbeAccessLogFilter(logging.Filter):
|
|
417
|
+
"""Drop benign, non-MCP HTTP probe noise from the uvicorn access log.
|
|
418
|
+
|
|
419
|
+
* ``GET``/``HEAD`` ``/favicon.ico`` -> ``404``: browsers auto-request a
|
|
420
|
+
favicon that doesn't exist. Pure noise, always dropped.
|
|
421
|
+
* ``GET``/``HEAD`` on the MCP path -> ``405``: a non-MCP caller (browser,
|
|
422
|
+
health check, reverse proxy, or a connector's SSE-style pre-flight) hit a
|
|
423
|
+
POST-only Streamable HTTP endpoint. The raw access line is dropped and the
|
|
424
|
+
landing handler logs one annotated "(NORMAL for most non-SSE connections)"
|
|
425
|
+
line in its place. Dropped only when ``drop_mcp_405`` is set — SSE callers
|
|
426
|
+
pass False, since there a GET answers 200 and a GET-405 is a genuine fault.
|
|
427
|
+
"""
|
|
428
|
+
|
|
429
|
+
def __init__(self, mcp_path: str, *, drop_mcp_405: bool = True) -> None:
|
|
430
|
+
super().__init__()
|
|
431
|
+
self._mcp_path = mcp_path.rstrip("/") or "/"
|
|
432
|
+
self._drop_mcp_405 = drop_mcp_405
|
|
433
|
+
|
|
434
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
435
|
+
# uvicorn.access records carry structured args: (client, method, path,
|
|
436
|
+
# http_version, status_int). Match on those, not the formatted string.
|
|
437
|
+
args = record.args
|
|
438
|
+
if not isinstance(args, tuple) or len(args) != 5:
|
|
439
|
+
return True
|
|
440
|
+
method, raw_path, status = args[1], args[2], args[4]
|
|
441
|
+
if method not in ("GET", "HEAD"):
|
|
442
|
+
return True
|
|
443
|
+
path = str(raw_path).split("?", 1)[0].rstrip("/") or "/"
|
|
444
|
+
if status == 404 and path == "/favicon.ico":
|
|
445
|
+
return False # browser favicon auto-request — pure noise
|
|
446
|
+
# By-design probe 405 on the MCP path; the handler logs an annotated line
|
|
447
|
+
# instead. This trusts that the landing route is the only GET/HEAD responder
|
|
448
|
+
# on the MCP path (true today). Kept in SSE mode (drop_mcp_405=False), where
|
|
449
|
+
# a GET answers 200 and a 405 is a real fault.
|
|
450
|
+
is_dropped_probe = (
|
|
451
|
+
status == 405 and path == self._mcp_path and self._drop_mcp_405
|
|
452
|
+
)
|
|
453
|
+
return not is_dropped_probe
|
|
454
|
+
|
|
455
|
+
|
|
405
456
|
def _setup_logging(log_level_str: str, force: bool = False) -> None:
|
|
406
457
|
"""Configure root logger with consistent timestamp format."""
|
|
407
458
|
logging.basicConfig(
|
|
@@ -532,9 +583,23 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
|
|
|
532
583
|
# Expected: we just cancelled server_task above; swallow its
|
|
533
584
|
# CancelledError so shutdown can proceed to cleanup.
|
|
534
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()
|
|
535
592
|
|
|
536
593
|
except asyncio.CancelledError:
|
|
537
|
-
|
|
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
|
|
538
603
|
finally:
|
|
539
604
|
try:
|
|
540
605
|
await asyncio.wait_for(
|
|
@@ -543,7 +608,12 @@ async def _run_with_shutdown(server_coro: Coroutine[Any, Any, Any]) -> None:
|
|
|
543
608
|
except TimeoutError:
|
|
544
609
|
logger.warning("Resource cleanup timed out")
|
|
545
610
|
|
|
546
|
-
|
|
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}")
|
|
547
617
|
|
|
548
618
|
|
|
549
619
|
def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
|
|
@@ -557,7 +627,7 @@ def _run_entrypoint(coro: Coroutine[Any, Any, Any], label: str) -> None:
|
|
|
557
627
|
except SystemExit:
|
|
558
628
|
raise
|
|
559
629
|
except Exception as e:
|
|
560
|
-
logger.error(f"{label} error: {e}")
|
|
630
|
+
logger.error(f"{label} error: {e}", exc_info=True)
|
|
561
631
|
sys.exit(1)
|
|
562
632
|
|
|
563
633
|
sys.exit(0)
|
|
@@ -858,7 +928,12 @@ async def _run_http_with_graceful_shutdown(
|
|
|
858
928
|
_registered_landing_paths: set[str] = set()
|
|
859
929
|
|
|
860
930
|
|
|
861
|
-
def register_browser_landing(
|
|
931
|
+
def register_browser_landing(
|
|
932
|
+
mcp_instance: "FastMCP | _DeferredMCP",
|
|
933
|
+
path: str,
|
|
934
|
+
*,
|
|
935
|
+
quiet_probe_log: bool = True,
|
|
936
|
+
) -> None:
|
|
862
937
|
"""Register a GET handler that returns 405 with a helpful message.
|
|
863
938
|
|
|
864
939
|
Browsers and misconfigured clients that send GET instead of POST will see
|
|
@@ -869,6 +944,10 @@ def register_browser_landing(mcp_instance: "FastMCP | _DeferredMCP", path: str)
|
|
|
869
944
|
Args:
|
|
870
945
|
mcp_instance: The FastMCP server to register the route on.
|
|
871
946
|
path: The MCP endpoint path (e.g. "/mcp" or a secret path).
|
|
947
|
+
quiet_probe_log: When True (default, for Streamable HTTP), drop the
|
|
948
|
+
by-design GET/HEAD-405 probe line on the MCP path from the uvicorn
|
|
949
|
+
access log (the handler logs an annotated replacement). Pass False
|
|
950
|
+
for SSE, where a GET answers 200 and a 405 is a genuine fault.
|
|
872
951
|
"""
|
|
873
952
|
if path in _registered_landing_paths:
|
|
874
953
|
logger.warning(
|
|
@@ -905,6 +984,12 @@ def register_browser_landing(mcp_instance: "FastMCP | _DeferredMCP", path: str)
|
|
|
905
984
|
# Custom routes are registered at lowest precedence (after the MCP route).
|
|
906
985
|
@mcp_instance.custom_route(path, methods=["GET"])
|
|
907
986
|
async def _browser_landing(_: Request) -> PlainTextResponse:
|
|
987
|
+
# Any GET here is a non-MCP caller (browser, health check, proxy, or a
|
|
988
|
+
# connector's SSE-style pre-flight) hitting this POST-only Streamable HTTP
|
|
989
|
+
# endpoint, which answers 405 by design. Log one annotated line so the 405
|
|
990
|
+
# reads as expected; ProbeAccessLogFilter drops the raw uvicorn access line
|
|
991
|
+
# so there's no cryptic duplicate.
|
|
992
|
+
logger.info("GET %s -> 405 (NORMAL for most non-SSE connections)", path)
|
|
908
993
|
return PlainTextResponse(
|
|
909
994
|
_landing_message,
|
|
910
995
|
status_code=405,
|
|
@@ -913,6 +998,16 @@ def register_browser_landing(mcp_instance: "FastMCP | _DeferredMCP", path: str)
|
|
|
913
998
|
headers={"Allow": "POST, DELETE"},
|
|
914
999
|
)
|
|
915
1000
|
|
|
1001
|
+
# Tidy uvicorn's access log: always drop browser favicon 404s, and drop the
|
|
1002
|
+
# raw by-design GET/HEAD-405 probe line on the MCP path (the handler above logs
|
|
1003
|
+
# an annotated replacement). The 405 drop is skipped for SSE
|
|
1004
|
+
# (quiet_probe_log=False), where a GET answers 200 and a 405 is a real fault.
|
|
1005
|
+
# Attach to uvicorn.access directly — it has propagate=False, so a root-logger
|
|
1006
|
+
# filter would miss it.
|
|
1007
|
+
logging.getLogger("uvicorn.access").addFilter(
|
|
1008
|
+
ProbeAccessLogFilter(path, drop_mcp_405=quiet_probe_log)
|
|
1009
|
+
)
|
|
1010
|
+
|
|
916
1011
|
|
|
917
1012
|
def _log_settings_url(host: str, port: int, path: str) -> None:
|
|
918
1013
|
"""Log the web settings-UI URL at HTTP startup.
|
|
@@ -946,7 +1041,9 @@ def _run_http_server(transport: str, default_port: int = 8086) -> None:
|
|
|
946
1041
|
|
|
947
1042
|
host, port, path = _get_http_runtime(default_port)
|
|
948
1043
|
_warn_if_default_path_exposed(host, port, path)
|
|
949
|
-
|
|
1044
|
+
# SSE transport answers GET with 200 (the event stream), so a GET->405 there
|
|
1045
|
+
# would be a real fault, not a benign probe — keep its access log intact.
|
|
1046
|
+
register_browser_landing(_get_mcp(), path, quiet_probe_log=transport != "sse")
|
|
950
1047
|
register_settings_routes(_get_mcp(), _get_server(), secret_path=path)
|
|
951
1048
|
_log_settings_url(host, port, path)
|
|
952
1049
|
|
|
@@ -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.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/resources/skills-vendor/LICENSE
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → ha_mcp_dev-7.7.0.dev699}/src/ha_mcp/transforms/categorized_search.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.7.0.dev697 → 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.dev697 → 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.dev697 → 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.dev697 → 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
|