ha-mcp-dev 7.12.3.dev1858__tar.gz → 7.12.3.dev1861__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.12.3.dev1858/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.12.3.dev1861}/PKG-INFO +1 -1
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/pyproject.toml +1 -1
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/config.py +56 -6
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/settings_ui/settings.js +5 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/config_entry_flow.py +155 -18
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_config.py +11 -5
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_deep.py +158 -81
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_fetch.py +49 -8
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_scenes.py +43 -5
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_config_helpers.py +1 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_integrations.py +4 -6
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/LICENSE +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/README.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/setup.cfg +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_mcp_component.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/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.12.3.
|
|
7
|
+
version = "7.12.3.dev1861"
|
|
8
8
|
description = "Home Assistant MCP Server - Complete control of Home Assistant through MCP"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# Upper bound widened to <3.15 so the package installs on Home Assistant's
|
|
@@ -83,6 +83,22 @@ class Settings(BaseSettings):
|
|
|
83
83
|
20.0, alias="HAMCP_SCENE_CONFIG_TIME_BUDGET"
|
|
84
84
|
)
|
|
85
85
|
|
|
86
|
+
# Per-request timeout and concurrency of the smart-search per-id
|
|
87
|
+
# config-fetch fallback (Attempt C). On HA servers that serve
|
|
88
|
+
# /config/<domain>/config/<id> serially, a full batch of concurrent
|
|
89
|
+
# requests queues behind one another and the tail of each batch can
|
|
90
|
+
# exceed the per-request timeout even though every request would
|
|
91
|
+
# succeed — lowering the batch size (toward 1) and/or raising the
|
|
92
|
+
# timeout lets such instances scan exhaustively (issue #1784). Same
|
|
93
|
+
# consumption model as the budgets above: import-time constants in
|
|
94
|
+
# tools/smart_search/_config.py, restart required.
|
|
95
|
+
individual_config_timeout: float = Field(
|
|
96
|
+
5.0, alias="HAMCP_INDIVIDUAL_CONFIG_TIMEOUT"
|
|
97
|
+
)
|
|
98
|
+
individual_fetch_batch_size: int = Field(
|
|
99
|
+
10, alias="HAMCP_INDIVIDUAL_FETCH_BATCH_SIZE"
|
|
100
|
+
)
|
|
101
|
+
|
|
86
102
|
# Backup tool configuration
|
|
87
103
|
backup_hint: str = Field("normal", alias="BACKUP_HINT")
|
|
88
104
|
|
|
@@ -350,21 +366,26 @@ class Settings(BaseSettings):
|
|
|
350
366
|
"automation_config_time_budget",
|
|
351
367
|
"script_config_time_budget",
|
|
352
368
|
"scene_config_time_budget",
|
|
369
|
+
"individual_config_timeout",
|
|
370
|
+
"individual_fetch_batch_size",
|
|
353
371
|
mode="before",
|
|
354
372
|
)
|
|
355
373
|
@classmethod
|
|
356
374
|
def _lenient_time_budget(cls, v: object, info: ValidationInfo) -> object:
|
|
357
|
-
"""Coerce the
|
|
358
|
-
|
|
375
|
+
"""Coerce the smart-search Attempt-C knobs (the three time budgets,
|
|
376
|
+
the per-request timeout, and the fetch batch size), falling back to
|
|
377
|
+
the field default (with a warning) instead of crashing startup.
|
|
359
378
|
|
|
360
379
|
Preserves the parse-tolerance of the removed ``_env_float`` helper
|
|
361
380
|
(empty / unparseable -> default) and additionally enforces the same
|
|
362
381
|
``_ADVANCED_SETTINGS_BOUNDS`` range as the override-file / UI-POST
|
|
363
382
|
path, so the env-var path can't smuggle in an out-of-range or
|
|
364
|
-
non-finite
|
|
365
|
-
per-id config-fetch scan, and ``inf`` / ``nan`` would
|
|
366
|
-
``lo <= val <= hi`` test rejects all three (NaN
|
|
367
|
-
False), keeping the env and override-file paths
|
|
383
|
+
non-finite value. A ``<= 0`` budget or timeout would silently
|
|
384
|
+
disable the per-id config-fetch scan, and ``inf`` / ``nan`` would
|
|
385
|
+
uncap it; the ``lo <= val <= hi`` test rejects all three (NaN
|
|
386
|
+
comparisons are False), keeping the env and override-file paths
|
|
387
|
+
consistent. Int fields (batch size) additionally reject fractional
|
|
388
|
+
values rather than truncating them."""
|
|
368
389
|
field_name = info.field_name
|
|
369
390
|
if field_name is None: # always set for field_validator; defensive
|
|
370
391
|
return v
|
|
@@ -389,6 +410,17 @@ class Settings(BaseSettings):
|
|
|
389
410
|
default,
|
|
390
411
|
)
|
|
391
412
|
return default
|
|
413
|
+
if isinstance(default, int) and not isinstance(default, bool):
|
|
414
|
+
if val != int(val):
|
|
415
|
+
logger.warning(
|
|
416
|
+
"Invalid value for %s=%r (must be a whole number); "
|
|
417
|
+
"using default %s",
|
|
418
|
+
field_name,
|
|
419
|
+
v,
|
|
420
|
+
default,
|
|
421
|
+
)
|
|
422
|
+
return default
|
|
423
|
+
return int(val)
|
|
392
424
|
return val
|
|
393
425
|
|
|
394
426
|
@property
|
|
@@ -759,6 +791,22 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
759
791
|
"search",
|
|
760
792
|
True,
|
|
761
793
|
),
|
|
794
|
+
# Attempt-C per-request timeout + batch size (#1784). Restart-required
|
|
795
|
+
# (same import-time consumption as the budgets above).
|
|
796
|
+
AdvancedField(
|
|
797
|
+
"individual_config_timeout",
|
|
798
|
+
"HAMCP_INDIVIDUAL_CONFIG_TIMEOUT",
|
|
799
|
+
float,
|
|
800
|
+
"search",
|
|
801
|
+
True,
|
|
802
|
+
),
|
|
803
|
+
AdvancedField(
|
|
804
|
+
"individual_fetch_batch_size",
|
|
805
|
+
"HAMCP_INDIVIDUAL_FETCH_BATCH_SIZE",
|
|
806
|
+
int,
|
|
807
|
+
"search",
|
|
808
|
+
True,
|
|
809
|
+
),
|
|
762
810
|
# Operations.
|
|
763
811
|
AdvancedField("backup_hint", "BACKUP_HINT", str, "operations", True),
|
|
764
812
|
AdvancedField("enable_websocket", "ENABLE_WEBSOCKET", bool, "operations", True),
|
|
@@ -846,6 +894,8 @@ _ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
|
|
|
846
894
|
"automation_config_time_budget": (1.0, 600.0),
|
|
847
895
|
"script_config_time_budget": (1.0, 600.0),
|
|
848
896
|
"scene_config_time_budget": (1.0, 600.0),
|
|
897
|
+
"individual_config_timeout": (1.0, 600.0),
|
|
898
|
+
"individual_fetch_batch_size": (1, 100),
|
|
849
899
|
"code_mode_max_duration": (1.0, 300.0),
|
|
850
900
|
"code_mode_max_memory": (1_048_576, 268_435_456),
|
|
851
901
|
"code_mode_max_recursion": (1, 10_000),
|
|
@@ -2946,6 +2946,8 @@ const ADVANCED_FIELD_META = {
|
|
|
2946
2946
|
automation_config_time_budget: { label: "Automation config time budget (s)", help: "Max seconds ha_search/ha_deep_search spends fetching automation configs before returning a partial result. Raise on instances with many automations. Range 1–600. Restart required." },
|
|
2947
2947
|
script_config_time_budget: { label: "Script config time budget (s)", help: "Max seconds ha_search/ha_deep_search spends fetching script configs before returning a partial result. Range 1–600. Restart required." },
|
|
2948
2948
|
scene_config_time_budget: { label: "Scene config time budget (s)", help: "Max seconds ha_search/ha_deep_search spends fetching scene configs before returning a partial result. Range 1–600. Restart required." },
|
|
2949
|
+
individual_config_timeout: { label: "Per-request config fetch timeout (s)", help: "Timeout for each individual automation/script/scene config fetch during deep search. On HA servers that serve config reads serially, raise this and/or lower the batch size so queued requests don't time out. Values above the HA request timeout (HA_TIMEOUT, default 30) have no extra effect — the HTTP client gives up first. Range 1–600. Restart required." },
|
|
2950
|
+
individual_fetch_batch_size: { label: "Config fetch batch size", help: "How many per-id config fetches deep search issues concurrently. Lower toward 1 on HA servers that serve config reads serially (symptom: 'timed out' partial-result warnings). Range 1–100. Restart required." },
|
|
2949
2951
|
backup_hint: { label: "Backup-hint level", help: "Tunes how strongly the LLM is prompted to take a full-HA snapshot before risky writes." },
|
|
2950
2952
|
dashboard_screenshot_engine_url: { label: "Dashboard screenshot engine URL", help: "Base URL of the screenshot engine (e.g. http://puppet:10000). Leave blank to auto-discover the Puppet App (add-on) via the Supervisor (HA OS / Supervised). Only used when the Dashboard Screenshot beta feature is enabled. Takes effect without a restart." },
|
|
2951
2953
|
enable_websocket: { label: "Enable WebSocket", help: "WebSocket-based state monitoring. Disabling falls back to polling; many tools degrade. Restart required." },
|
|
@@ -2985,6 +2987,9 @@ const ADVANCED_RESTART_REQUIRED = new Set([
|
|
|
2985
2987
|
// it is resolved live per capture, so it takes effect immediately.
|
|
2986
2988
|
"automation_config_time_budget", "script_config_time_budget",
|
|
2987
2989
|
"scene_config_time_budget",
|
|
2990
|
+
// The Attempt-C per-request timeout and batch size (#1784) share the
|
|
2991
|
+
// budgets' import-time consumption, so they need a restart too.
|
|
2992
|
+
"individual_config_timeout", "individual_fetch_batch_size",
|
|
2988
2993
|
"code_mode_max_duration", "code_mode_max_memory",
|
|
2989
2994
|
"code_mode_max_recursion", "code_mode_max_invocations",
|
|
2990
2995
|
"code_mode_saved_tools_path",
|
{ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/config_entry_flow.py
RENAMED
|
@@ -12,6 +12,7 @@ for the 15 helper types listed in FLOW_HELPER_TYPES.
|
|
|
12
12
|
|
|
13
13
|
import asyncio
|
|
14
14
|
import logging
|
|
15
|
+
from collections.abc import Iterator
|
|
15
16
|
from enum import StrEnum
|
|
16
17
|
from typing import Any, Literal
|
|
17
18
|
|
|
@@ -128,10 +129,125 @@ def _handle_menu_step(
|
|
|
128
129
|
return str(menu_choice)
|
|
129
130
|
|
|
130
131
|
|
|
132
|
+
def iter_schema_fields(data_schema: Any) -> Iterator[dict[str, Any]]:
|
|
133
|
+
"""Yield leaf field definitions from a flow schema, descending into sections."""
|
|
134
|
+
if not isinstance(data_schema, list):
|
|
135
|
+
return
|
|
136
|
+
for field in data_schema:
|
|
137
|
+
if not isinstance(field, dict):
|
|
138
|
+
continue
|
|
139
|
+
nested_schema = field.get("schema")
|
|
140
|
+
if isinstance(nested_schema, list):
|
|
141
|
+
yield from iter_schema_fields(nested_schema)
|
|
142
|
+
continue
|
|
143
|
+
yield field
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _section_path(path_prefix: str, name: Any) -> str:
|
|
147
|
+
"""Return the dotted config path for a named or anonymous section."""
|
|
148
|
+
if not isinstance(name, str):
|
|
149
|
+
return path_prefix
|
|
150
|
+
return f"{path_prefix}.{name}" if path_prefix else name
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _record_ignored_section_keys(
|
|
154
|
+
ignored_config_keys: set[str] | None,
|
|
155
|
+
remaining_config: dict[str, Any],
|
|
156
|
+
section_path: str,
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Record undeclared keys remaining inside an explicit section dict."""
|
|
159
|
+
if ignored_config_keys is None:
|
|
160
|
+
return
|
|
161
|
+
ignored_config_keys.update(
|
|
162
|
+
f"{section_path}.{key}" if section_path else key
|
|
163
|
+
for key in remaining_config
|
|
164
|
+
if key not in _MENU_SELECTION_KEYS
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _consume_form_schema(
|
|
169
|
+
data_schema: list[Any],
|
|
170
|
+
remaining_config: dict[str, Any],
|
|
171
|
+
ignored_config_keys: set[str] | None = None,
|
|
172
|
+
path_prefix: str = "",
|
|
173
|
+
) -> dict[str, Any]:
|
|
174
|
+
"""Consume matching config values and shape nested flow sections.
|
|
175
|
+
|
|
176
|
+
Mutates ``remaining_config`` by removing every consumed key. Flat child
|
|
177
|
+
values override the same value inside an explicitly supplied section dict.
|
|
178
|
+
Unknown keys inside explicit section dicts are added to
|
|
179
|
+
``ignored_config_keys`` with their dotted section path.
|
|
180
|
+
"""
|
|
181
|
+
form_data: dict[str, Any] = {}
|
|
182
|
+
missing = object()
|
|
183
|
+
|
|
184
|
+
for field in data_schema:
|
|
185
|
+
if not isinstance(field, dict):
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
name = field.get("name")
|
|
189
|
+
nested_schema = field.get("schema")
|
|
190
|
+
if isinstance(nested_schema, list):
|
|
191
|
+
section_path = _section_path(path_prefix, name)
|
|
192
|
+
explicit_section = (
|
|
193
|
+
remaining_config.pop(name, missing)
|
|
194
|
+
if isinstance(name, str)
|
|
195
|
+
else missing
|
|
196
|
+
)
|
|
197
|
+
if explicit_section is not missing and not isinstance(
|
|
198
|
+
explicit_section, dict
|
|
199
|
+
):
|
|
200
|
+
if isinstance(name, str):
|
|
201
|
+
form_data[name] = explicit_section
|
|
202
|
+
continue
|
|
203
|
+
|
|
204
|
+
nested_data: dict[str, Any] = {}
|
|
205
|
+
if isinstance(explicit_section, dict):
|
|
206
|
+
explicit_remaining = dict(explicit_section)
|
|
207
|
+
nested_data.update(
|
|
208
|
+
_consume_form_schema(
|
|
209
|
+
nested_schema,
|
|
210
|
+
explicit_remaining,
|
|
211
|
+
ignored_config_keys,
|
|
212
|
+
section_path,
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
_record_ignored_section_keys(
|
|
216
|
+
ignored_config_keys,
|
|
217
|
+
explicit_remaining,
|
|
218
|
+
section_path,
|
|
219
|
+
)
|
|
220
|
+
nested_data.update(
|
|
221
|
+
_consume_form_schema(
|
|
222
|
+
nested_schema,
|
|
223
|
+
remaining_config,
|
|
224
|
+
ignored_config_keys,
|
|
225
|
+
section_path,
|
|
226
|
+
)
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if nested_data:
|
|
230
|
+
if isinstance(name, str):
|
|
231
|
+
form_data[name] = nested_data
|
|
232
|
+
else:
|
|
233
|
+
form_data.update(nested_data)
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
isinstance(name, str)
|
|
238
|
+
and name not in _MENU_SELECTION_KEYS
|
|
239
|
+
and name in remaining_config
|
|
240
|
+
):
|
|
241
|
+
form_data[name] = remaining_config.pop(name)
|
|
242
|
+
|
|
243
|
+
return form_data
|
|
244
|
+
|
|
245
|
+
|
|
131
246
|
def _extract_schema_field_names(data_schema: Any) -> set[str] | None:
|
|
132
247
|
"""Extract the set of field names declared by a step's data_schema.
|
|
133
248
|
|
|
134
249
|
HA returns data_schema as a list of {name, selector, required, ...} dicts.
|
|
250
|
+
Nested leaf names are included; section-container names are omitted.
|
|
135
251
|
Returns ``None`` when the schema is absent or not a list (signalling
|
|
136
252
|
the caller to fall back to legacy submit-all behaviour). Returns a
|
|
137
253
|
(possibly empty) set when the schema is present and parseable.
|
|
@@ -139,11 +255,10 @@ def _extract_schema_field_names(data_schema: Any) -> set[str] | None:
|
|
|
139
255
|
if not isinstance(data_schema, list):
|
|
140
256
|
return None
|
|
141
257
|
names: set[str] = set()
|
|
142
|
-
for field in data_schema:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
names.add(name)
|
|
258
|
+
for field in iter_schema_fields(data_schema):
|
|
259
|
+
name = field.get("name")
|
|
260
|
+
if isinstance(name, str):
|
|
261
|
+
names.add(name)
|
|
147
262
|
return names
|
|
148
263
|
|
|
149
264
|
|
|
@@ -151,13 +266,15 @@ def _handle_form_step(
|
|
|
151
266
|
flow_id: str,
|
|
152
267
|
current_step: dict[str, Any],
|
|
153
268
|
remaining_config: dict[str, Any],
|
|
269
|
+
ignored_config_keys: set[str] | None = None,
|
|
154
270
|
) -> dict[str, Any]:
|
|
155
271
|
"""Validate a form step and return form data to submit.
|
|
156
272
|
|
|
157
273
|
When the step's ``data_schema`` is provided, pops ONLY the keys declared
|
|
158
274
|
in that schema from ``remaining_config`` (mutating it) so any unconsumed
|
|
159
275
|
keys remain available for subsequent steps. Menu selection keys are never
|
|
160
|
-
submitted.
|
|
276
|
+
submitted. Fields declared inside a section are grouped under the section
|
|
277
|
+
key; callers may provide them flat or inside an explicit section dict.
|
|
161
278
|
|
|
162
279
|
When ``data_schema`` is absent (HA didn't tell us field names), falls
|
|
163
280
|
back to legacy behaviour: submit all non-menu keys and clear them. This
|
|
@@ -180,10 +297,10 @@ def _handle_form_step(
|
|
|
180
297
|
)
|
|
181
298
|
)
|
|
182
299
|
|
|
183
|
-
|
|
300
|
+
data_schema = current_step.get("data_schema")
|
|
184
301
|
|
|
185
302
|
form_data: dict[str, Any] = {}
|
|
186
|
-
if
|
|
303
|
+
if not isinstance(data_schema, list):
|
|
187
304
|
# Legacy fallback: no schema info — dump every non-menu key and
|
|
188
305
|
# consume them all so a follow-up step (rare without schema) won't
|
|
189
306
|
# re-submit the same data.
|
|
@@ -192,11 +309,9 @@ def _handle_form_step(
|
|
|
192
309
|
continue
|
|
193
310
|
form_data[key] = remaining_config.pop(key)
|
|
194
311
|
else:
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if key in schema_fields:
|
|
199
|
-
form_data[key] = remaining_config.pop(key)
|
|
312
|
+
form_data = _consume_form_schema(
|
|
313
|
+
data_schema, remaining_config, ignored_config_keys
|
|
314
|
+
)
|
|
200
315
|
|
|
201
316
|
return form_data
|
|
202
317
|
|
|
@@ -512,7 +627,8 @@ async def _handle_flow_steps(
|
|
|
512
627
|
for unstructured HA 4xx responses so the caller can react.
|
|
513
628
|
|
|
514
629
|
Returns:
|
|
515
|
-
``{"success": True, "entry": result}`` on success
|
|
630
|
+
``{"success": True, "entry": result}`` on success, plus ``warnings``
|
|
631
|
+
when caller-supplied config keys were not declared by any flow step.
|
|
516
632
|
Raises ToolError on any failure.
|
|
517
633
|
"""
|
|
518
634
|
if submit_fn is None:
|
|
@@ -520,13 +636,23 @@ async def _handle_flow_steps(
|
|
|
520
636
|
remaining_config = dict(config)
|
|
521
637
|
current_step = initial_step
|
|
522
638
|
last_menu_choice: str | None = None
|
|
639
|
+
ignored_config_keys: set[str] = set()
|
|
523
640
|
max_steps = 10
|
|
524
641
|
|
|
525
642
|
for step_num in range(max_steps):
|
|
526
643
|
result_type = current_step.get("type")
|
|
527
644
|
|
|
528
645
|
if result_type == _FlowType.CREATE_ENTRY:
|
|
529
|
-
|
|
646
|
+
ignored_config_keys.update(
|
|
647
|
+
key for key in remaining_config if key not in _MENU_SELECTION_KEYS
|
|
648
|
+
)
|
|
649
|
+
response: dict[str, Any] = {"success": True, "entry": current_step}
|
|
650
|
+
if ignored_config_keys:
|
|
651
|
+
response["warnings"] = [
|
|
652
|
+
"Ignored config keys not declared by the Home Assistant flow "
|
|
653
|
+
f"schema: {', '.join(sorted(ignored_config_keys))}"
|
|
654
|
+
]
|
|
655
|
+
return response
|
|
530
656
|
|
|
531
657
|
if result_type == _FlowType.ABORT:
|
|
532
658
|
raise_tool_error(
|
|
@@ -559,7 +685,12 @@ async def _handle_flow_steps(
|
|
|
559
685
|
# step's data_schema, leaving any other keys in remaining_config
|
|
560
686
|
# for subsequent steps (HA can present multi-step forms, e.g.
|
|
561
687
|
# statistics: user step then pick-characteristic step).
|
|
562
|
-
form_data = _handle_form_step(
|
|
688
|
+
form_data = _handle_form_step(
|
|
689
|
+
flow_id,
|
|
690
|
+
current_step,
|
|
691
|
+
remaining_config,
|
|
692
|
+
ignored_config_keys,
|
|
693
|
+
)
|
|
563
694
|
logger.debug(
|
|
564
695
|
f"Flow step {step_num}: form submit "
|
|
565
696
|
f"(step_id={current_step.get('step_id')}, keys={list(form_data.keys())})"
|
|
@@ -875,7 +1006,7 @@ async def update_flow_helper(
|
|
|
875
1006
|
raise
|
|
876
1007
|
|
|
877
1008
|
entry = result["entry"].get("result", {})
|
|
878
|
-
|
|
1009
|
+
response = {
|
|
879
1010
|
"success": True,
|
|
880
1011
|
"entry_id": entry_id,
|
|
881
1012
|
"title": entry.get("title"),
|
|
@@ -883,6 +1014,9 @@ async def update_flow_helper(
|
|
|
883
1014
|
"message": f"{helper_type} helper updated successfully",
|
|
884
1015
|
"updated": True,
|
|
885
1016
|
}
|
|
1017
|
+
if result.get("warnings"):
|
|
1018
|
+
response["warnings"] = result["warnings"]
|
|
1019
|
+
return response
|
|
886
1020
|
|
|
887
1021
|
|
|
888
1022
|
async def create_flow_helper(
|
|
@@ -928,10 +1062,13 @@ async def create_flow_helper(
|
|
|
928
1062
|
raise
|
|
929
1063
|
|
|
930
1064
|
entry = result["entry"].get("result", {})
|
|
931
|
-
|
|
1065
|
+
response = {
|
|
932
1066
|
"success": True,
|
|
933
1067
|
"entry_id": entry.get("entry_id"),
|
|
934
1068
|
"title": entry.get("title"),
|
|
935
1069
|
"domain": helper_type,
|
|
936
1070
|
"message": f"{helper_type} helper created successfully",
|
|
937
1071
|
}
|
|
1072
|
+
if result.get("warnings"):
|
|
1073
|
+
response["warnings"] = result["warnings"]
|
|
1074
|
+
return response
|
{ha_mcp_dev-7.12.3.dev1858 → ha_mcp_dev-7.12.3.dev1861}/src/ha_mcp/tools/smart_search/_config.py
RENAMED
|
@@ -13,23 +13,29 @@ DEFAULT_CONCURRENCY_LIMIT = 20
|
|
|
13
13
|
# Bulk fetch timeouts (in seconds)
|
|
14
14
|
BULK_REST_TIMEOUT = 5.0 # Timeout for bulk REST endpoint calls
|
|
15
15
|
BULK_WEBSOCKET_TIMEOUT = 3.0 # Timeout for bulk WebSocket calls
|
|
16
|
-
INDIVIDUAL_CONFIG_TIMEOUT = 5.0 # Timeout for individual config fetches
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
18
|
+
# Attempt-C (per-id fallback) tuning knobs. Sourced from the resolved
|
|
19
|
+
# Settings (issues #1538 / #1784) so the env var, the web Settings UI
|
|
21
20
|
# override file, and the field defaults all flow through one precedence path
|
|
22
21
|
# — and so add-on users (who cannot set raw env vars) can tune them from the
|
|
23
22
|
# Advanced panel. Read once at import as module-level constants; a change
|
|
24
23
|
# takes effect on the next MCP-host restart (advanced settings already
|
|
25
24
|
# carry a restart-required notice in the UI).
|
|
26
25
|
_settings = get_global_settings()
|
|
26
|
+
|
|
27
|
+
# Time budgets for fallback individual fetching (in seconds).
|
|
27
28
|
AUTOMATION_CONFIG_TIME_BUDGET = _settings.automation_config_time_budget
|
|
28
29
|
SCRIPT_CONFIG_TIME_BUDGET = _settings.script_config_time_budget
|
|
29
30
|
SCENE_CONFIG_TIME_BUDGET = _settings.scene_config_time_budget
|
|
30
31
|
|
|
31
|
-
#
|
|
32
|
-
|
|
32
|
+
# Per-request timeout (seconds) and batch size for the parallel individual
|
|
33
|
+
# config fetches. On HA servers that serve the per-id config endpoint
|
|
34
|
+
# serially, a batch's tail requests queue behind its head and can exceed
|
|
35
|
+
# the per-request timeout despite being perfectly healthy — tune batch
|
|
36
|
+
# size toward 1 and/or raise the timeout on such instances (issue #1784).
|
|
37
|
+
INDIVIDUAL_CONFIG_TIMEOUT = _settings.individual_config_timeout
|
|
38
|
+
INDIVIDUAL_FETCH_BATCH_SIZE = _settings.individual_fetch_batch_size
|
|
33
39
|
|
|
34
40
|
|
|
35
41
|
def _simplify_states_summary(
|