ha-mcp-dev 7.14.2.dev2093__tar.gz → 7.14.2.dev2095__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.14.2.dev2093/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2095}/PKG-INFO +1 -1
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/pyproject.toml +1 -1
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/paths.py +66 -40
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_bug_report.py +36 -4
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_dashboards.py +165 -36
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/setup.cfg +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/de.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/en.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_deep.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/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.14.2.
|
|
7
|
+
version = "7.14.2.dev2095"
|
|
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
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/paths.py
RENAMED
|
@@ -347,42 +347,28 @@ async def fetch_dashboard_render_config(
|
|
|
347
347
|
return cast(dict[str, Any], config)
|
|
348
348
|
|
|
349
349
|
|
|
350
|
-
def
|
|
350
|
+
def match_dashboard_view(
|
|
351
351
|
dashboard_url_path: str,
|
|
352
352
|
config: dict[str, Any],
|
|
353
|
-
view_path: str
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
"No view_path was supplied and the dashboard has no static views; "
|
|
373
|
-
"only the base route is available."
|
|
374
|
-
)
|
|
375
|
-
return DashboardRenderTarget(
|
|
376
|
-
dashboard_url_path=base_path,
|
|
377
|
-
view_path=None,
|
|
378
|
-
render_path=base_path,
|
|
379
|
-
view_index=None,
|
|
380
|
-
stable=False,
|
|
381
|
-
warnings=(warning,),
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
cleaned_view_path = view_path
|
|
385
|
-
if not cleaned_view_path.strip():
|
|
353
|
+
view_path: str,
|
|
354
|
+
*,
|
|
355
|
+
strategy_suggestions: tuple[str, ...] = (
|
|
356
|
+
"Use dashboard_path with the frontend route instead",
|
|
357
|
+
),
|
|
358
|
+
) -> tuple[int, dict[str, Any]]:
|
|
359
|
+
"""Match one named Lovelace view by its configured ``views[].path``.
|
|
360
|
+
|
|
361
|
+
Shared by screenshot view resolution and get-mode view scoping so both
|
|
362
|
+
honour identical semantics: exact match on the configured path, with
|
|
363
|
+
structured errors for an empty path, a strategy dashboard (no static
|
|
364
|
+
views to match), and a missing/ambiguous path (the not-found error lists
|
|
365
|
+
the available paths). Returns ``(view_index, view)``.
|
|
366
|
+
|
|
367
|
+
``strategy_suggestions`` lets each caller phrase the strategy-dashboard
|
|
368
|
+
remediation in its own vocabulary (the screenshot tools have a
|
|
369
|
+
``dashboard_path`` route parameter; config scoping does not).
|
|
370
|
+
"""
|
|
371
|
+
if not view_path.strip():
|
|
386
372
|
raise_tool_error(
|
|
387
373
|
create_error_response(
|
|
388
374
|
ErrorCode.VALIDATION_INVALID_PARAMETER,
|
|
@@ -397,9 +383,9 @@ def resolve_dashboard_view(
|
|
|
397
383
|
"Strategy dashboards do not expose static named view paths.",
|
|
398
384
|
context={
|
|
399
385
|
"dashboard_url_path": dashboard_url_path,
|
|
400
|
-
"view_path":
|
|
386
|
+
"view_path": view_path,
|
|
401
387
|
},
|
|
402
|
-
suggestions=
|
|
388
|
+
suggestions=list(strategy_suggestions),
|
|
403
389
|
)
|
|
404
390
|
)
|
|
405
391
|
views = config.get("views")
|
|
@@ -408,7 +394,7 @@ def resolve_dashboard_view(
|
|
|
408
394
|
matches = [
|
|
409
395
|
(index, view)
|
|
410
396
|
for index, view in enumerate(views)
|
|
411
|
-
if _configured_view_path(view) ==
|
|
397
|
+
if _configured_view_path(view) == view_path
|
|
412
398
|
]
|
|
413
399
|
if len(matches) != 1:
|
|
414
400
|
available = [
|
|
@@ -420,17 +406,57 @@ def resolve_dashboard_view(
|
|
|
420
406
|
raise_tool_error(
|
|
421
407
|
create_error_response(
|
|
422
408
|
ErrorCode.RESOURCE_NOT_FOUND,
|
|
423
|
-
f"Dashboard view path '{
|
|
409
|
+
f"Dashboard view path '{view_path}' is {reason}.",
|
|
424
410
|
context={
|
|
425
411
|
"dashboard_url_path": dashboard_url_path,
|
|
426
|
-
"view_path":
|
|
412
|
+
"view_path": view_path,
|
|
427
413
|
"available_view_paths": available,
|
|
428
414
|
},
|
|
429
415
|
suggestions=["Use a view_path returned by ha_config_get_dashboard"],
|
|
430
416
|
)
|
|
431
417
|
)
|
|
418
|
+
return matches[0]
|
|
419
|
+
|
|
432
420
|
|
|
433
|
-
|
|
421
|
+
def resolve_dashboard_view(
|
|
422
|
+
dashboard_url_path: str,
|
|
423
|
+
config: dict[str, Any],
|
|
424
|
+
view_path: str | None,
|
|
425
|
+
) -> DashboardRenderTarget:
|
|
426
|
+
"""Resolve one named Lovelace view to a canonical frontend route."""
|
|
427
|
+
base_path = dashboard_frontend_path(dashboard_url_path)
|
|
428
|
+
if view_path is None:
|
|
429
|
+
views = config.get("views")
|
|
430
|
+
has_static_views = isinstance(views, list) and bool(views)
|
|
431
|
+
if "strategy" in config:
|
|
432
|
+
warning = (
|
|
433
|
+
"No view_path was supplied; this strategy dashboard generates "
|
|
434
|
+
"its views at runtime, so only the base route is available."
|
|
435
|
+
)
|
|
436
|
+
elif has_static_views:
|
|
437
|
+
warning = (
|
|
438
|
+
"No view_path was supplied; the dashboard base route renders "
|
|
439
|
+
"the first view visible to Puppet's Home Assistant user."
|
|
440
|
+
)
|
|
441
|
+
else:
|
|
442
|
+
warning = (
|
|
443
|
+
"No view_path was supplied and the dashboard has no static views; "
|
|
444
|
+
"only the base route is available."
|
|
445
|
+
)
|
|
446
|
+
return DashboardRenderTarget(
|
|
447
|
+
dashboard_url_path=base_path,
|
|
448
|
+
view_path=None,
|
|
449
|
+
render_path=base_path,
|
|
450
|
+
view_index=None,
|
|
451
|
+
stable=False,
|
|
452
|
+
warnings=(warning,),
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
view_index, _ = match_dashboard_view(dashboard_url_path, config, view_path)
|
|
456
|
+
cleaned_view_path = view_path
|
|
457
|
+
views = config.get("views")
|
|
458
|
+
if not isinstance(views, list):
|
|
459
|
+
views = []
|
|
434
460
|
render_path = _safe_named_render_path(base_path, cleaned_view_path)
|
|
435
461
|
if render_path is None:
|
|
436
462
|
return _fallback_view_target(
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_bug_report.py
RENAMED
|
@@ -34,7 +34,7 @@ from ..utils.usage_logger import (
|
|
|
34
34
|
)
|
|
35
35
|
from .component_api import get_component_caps
|
|
36
36
|
from .helpers import log_tool_usage, register_tool_methods
|
|
37
|
-
from .util_helpers import ANSI_ESCAPE_RE
|
|
37
|
+
from .util_helpers import ANSI_ESCAPE_RE, JSON_STRING_COERCION, project_fields
|
|
38
38
|
|
|
39
39
|
logger = logging.getLogger(__name__)
|
|
40
40
|
|
|
@@ -676,10 +676,38 @@ class BugReportTools:
|
|
|
676
676
|
),
|
|
677
677
|
),
|
|
678
678
|
] = 10,
|
|
679
|
+
fields: Annotated[
|
|
680
|
+
str | list[str] | None,
|
|
681
|
+
JSON_STRING_COERCION,
|
|
682
|
+
Field(
|
|
683
|
+
default=None,
|
|
684
|
+
description=(
|
|
685
|
+
"Return only the specified top-level response keys — the "
|
|
686
|
+
"full response (both templates + logs + diagnostics, with "
|
|
687
|
+
"log content repeated across the raw keys and templates) "
|
|
688
|
+
"is very large. "
|
|
689
|
+
"None = full response. Typical for a runtime bug: "
|
|
690
|
+
"'runtime_bug_template,suggested_title,"
|
|
691
|
+
"runtime_bug_submit_url,duplicate_check_urls,"
|
|
692
|
+
"anonymization_guide,instructions'; for agent feedback "
|
|
693
|
+
"swap in agent_behavior_template and "
|
|
694
|
+
"agent_behavior_submit_url. The templates already embed "
|
|
695
|
+
"the relevant logs, so the raw log keys are only needed "
|
|
696
|
+
"for your own analysis. "
|
|
697
|
+
"Available keys: diagnostic_info, recent_logs, "
|
|
698
|
+
"startup_logs, addon_logs, core_error_log, log_count, "
|
|
699
|
+
"startup_log_count, formatted_report, "
|
|
700
|
+
"runtime_bug_template, agent_behavior_template, "
|
|
701
|
+
"anonymization_guide, suggested_title, "
|
|
702
|
+
"runtime_bug_submit_url, agent_behavior_submit_url, "
|
|
703
|
+
"duplicate_check_urls, missing_tool_hint, instructions."
|
|
704
|
+
),
|
|
705
|
+
),
|
|
706
|
+
] = None,
|
|
679
707
|
ctx: Context | None = None,
|
|
680
708
|
) -> dict[str, Any]:
|
|
681
709
|
"""
|
|
682
|
-
|
|
710
|
+
Get diagnostic information and templates for filing issue reports or feedback.
|
|
683
711
|
|
|
684
712
|
This tool generates templates for TWO types of reports:
|
|
685
713
|
1. **Runtime Bug Report** - For ha-mcp errors, failures, unexpected behavior
|
|
@@ -709,7 +737,10 @@ class BugReportTools:
|
|
|
709
737
|
- "That was inefficient"
|
|
710
738
|
|
|
711
739
|
**OUTPUT:**
|
|
712
|
-
Returns both templates plus diagnostic data.
|
|
740
|
+
Returns both templates plus diagnostic data. The full response is
|
|
741
|
+
LARGE (the captured logs appear in the raw log keys AND inside each
|
|
742
|
+
template) — pass fields=... to fetch only the keys you need once you
|
|
743
|
+
know which template applies. Key fields:
|
|
713
744
|
- `runtime_bug_template`, `agent_behavior_template` — pick based on context
|
|
714
745
|
- `recent_logs`, `startup_logs` — captured ha-mcp tool/server log entries
|
|
715
746
|
- `addon_logs` — addon container stdout/stderr (HA add-on installs only;
|
|
@@ -843,7 +874,7 @@ class BugReportTools:
|
|
|
843
874
|
for keyword in search_keywords[:3] # Limit to top 3 keywords
|
|
844
875
|
]
|
|
845
876
|
|
|
846
|
-
|
|
877
|
+
result: dict[str, Any] = {
|
|
847
878
|
"success": True,
|
|
848
879
|
"diagnostic_info": diagnostic_info,
|
|
849
880
|
"recent_logs": recent_logs,
|
|
@@ -934,6 +965,7 @@ class BugReportTools:
|
|
|
934
965
|
"CRITICAL: Always ANONYMIZE the report BEFORE presenting it in markdown code blocks!"
|
|
935
966
|
),
|
|
936
967
|
}
|
|
968
|
+
return project_fields(result, fields)
|
|
937
969
|
|
|
938
970
|
|
|
939
971
|
def register_bug_report_tools(mcp: Any, client: Any, **kwargs: Any) -> None:
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/tools/tools_config_dashboards.py
RENAMED
|
@@ -37,6 +37,7 @@ from ..dashboard_screenshot.content import (
|
|
|
37
37
|
from ..dashboard_screenshot.paths import (
|
|
38
38
|
dashboard_frontend_path,
|
|
39
39
|
dashboard_render_paths,
|
|
40
|
+
match_dashboard_view,
|
|
40
41
|
resolve_dashboard_view,
|
|
41
42
|
)
|
|
42
43
|
from ..errors import ErrorCode, create_error_response
|
|
@@ -1507,19 +1508,21 @@ def _note_screenshot_ignored(
|
|
|
1507
1508
|
options: _DashboardScreenshotOptions | None = None,
|
|
1508
1509
|
mode: str,
|
|
1509
1510
|
) -> None:
|
|
1510
|
-
"""Warn when
|
|
1511
|
-
|
|
1512
|
-
Screenshot options
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1511
|
+
"""Warn when get-mode-only options are passed to a mode that ignores them.
|
|
1512
|
+
|
|
1513
|
+
Screenshot options — and ``view_path``, which is a get-mode scoping
|
|
1514
|
+
parameter that merely travels inside the options dataclass — are only
|
|
1515
|
+
honoured in get mode. In list and search mode they are accepted but
|
|
1516
|
+
inapplicable, so surface a ``warnings`` entry rather than dropping the
|
|
1517
|
+
request as a silent no-op (matches the warn-don't-fail contract the
|
|
1518
|
+
params document)."""
|
|
1516
1519
|
capture_options = options or _DashboardScreenshotOptions(full_page=full_page)
|
|
1517
1520
|
if include_screenshot or capture_options != _DashboardScreenshotOptions():
|
|
1518
1521
|
result.setdefault("warnings", []).append(
|
|
1519
|
-
f"include_screenshot and screenshot render options are
|
|
1520
|
-
"mode; call "
|
|
1522
|
+
f"include_screenshot, view_path, and screenshot render options are "
|
|
1523
|
+
f"ignored in {mode} mode; call "
|
|
1521
1524
|
"ha_config_get_dashboard with a url_path (and no search criteria) "
|
|
1522
|
-
"to get a screenshot."
|
|
1525
|
+
"to scope to a view or get a screenshot."
|
|
1523
1526
|
)
|
|
1524
1527
|
|
|
1525
1528
|
|
|
@@ -1832,8 +1835,13 @@ class DashboardConfigTools:
|
|
|
1832
1835
|
view_path: Annotated[
|
|
1833
1836
|
str | None,
|
|
1834
1837
|
Field(
|
|
1835
|
-
description="
|
|
1836
|
-
"views[].path
|
|
1838
|
+
description="Get mode: return ONLY the view whose "
|
|
1839
|
+
"Lovelace views[].path matches (response carries 'view' + "
|
|
1840
|
+
"'view_index' instead of the full 'config') — use this to keep "
|
|
1841
|
+
"multi-view dashboards from blowing up the response when you "
|
|
1842
|
+
"only need one view. Does not require any beta feature. With "
|
|
1843
|
+
"include_screenshot, also selects the view to render. Ignored "
|
|
1844
|
+
"in list/search mode. Omit for the full config."
|
|
1837
1845
|
),
|
|
1838
1846
|
] = None,
|
|
1839
1847
|
mode: Annotated[
|
|
@@ -1880,6 +1888,13 @@ class DashboardConfigTools:
|
|
|
1880
1888
|
MODE 3 — Get: Active when list_only=False and no search parameters are provided.
|
|
1881
1889
|
Returns the full Lovelace dashboard config, defaulting to the
|
|
1882
1890
|
main dashboard if url_path is omitted.
|
|
1891
|
+
Pass view_path=<views[].path> to return ONLY that view: the response
|
|
1892
|
+
then carries `view` + `view_index` instead of `config`, keeping the
|
|
1893
|
+
payload small on multi-view dashboards. `config_hash` still covers
|
|
1894
|
+
the FULL config, so a follow-up
|
|
1895
|
+
ha_config_set_dashboard(python_transform=...) addressing
|
|
1896
|
+
config['views'][view_index] validates unchanged. An unknown
|
|
1897
|
+
view_path errors and lists the available view paths.
|
|
1883
1898
|
|
|
1884
1899
|
MODE 4 — Search all: mode="search" with query=<entity_id or text>
|
|
1885
1900
|
Answers "which dashboards contain this entity/card" by walking every
|
|
@@ -1900,6 +1915,7 @@ class DashboardConfigTools:
|
|
|
1900
1915
|
- List all dashboards: ha_config_get_dashboard(list_only=True)
|
|
1901
1916
|
- Get default dashboard: ha_config_get_dashboard(url_path="default")
|
|
1902
1917
|
- Get custom dashboard: ha_config_get_dashboard(url_path="lovelace-mobile")
|
|
1918
|
+
- Get one view only: ha_config_get_dashboard(url_path="lovelace-mobile", view_path="office")
|
|
1903
1919
|
- Force reload: ha_config_get_dashboard(url_path="lovelace-home", force_reload=True)
|
|
1904
1920
|
- Find cards by entity: ha_config_get_dashboard(url_path="my-dash", entity_id="light.living_room")
|
|
1905
1921
|
- Find by wildcard: ha_config_get_dashboard(url_path="my-dash", entity_id="sensor.temperature_*")
|
|
@@ -1929,7 +1945,11 @@ class DashboardConfigTools:
|
|
|
1929
1945
|
try:
|
|
1930
1946
|
if mode == "search":
|
|
1931
1947
|
# Cross-dashboard search takes precedence over every other mode.
|
|
1932
|
-
return await self._get_dashboard_search_all_mode(
|
|
1948
|
+
return await self._get_dashboard_search_all_mode(
|
|
1949
|
+
query,
|
|
1950
|
+
include_screenshot=include_screenshot,
|
|
1951
|
+
screenshot_options=screenshot_options,
|
|
1952
|
+
)
|
|
1933
1953
|
|
|
1934
1954
|
if list_only:
|
|
1935
1955
|
return await self._get_dashboard_list_mode(
|
|
@@ -1972,6 +1992,7 @@ class DashboardConfigTools:
|
|
|
1972
1992
|
url_path,
|
|
1973
1993
|
resolved_url_path=resolved_url_path,
|
|
1974
1994
|
force_reload=force_reload,
|
|
1995
|
+
view_path=view_path,
|
|
1975
1996
|
include_screenshot=include_screenshot,
|
|
1976
1997
|
screenshot_options=screenshot_options,
|
|
1977
1998
|
)
|
|
@@ -2283,7 +2304,13 @@ class DashboardConfigTools:
|
|
|
2283
2304
|
)
|
|
2284
2305
|
return search_result
|
|
2285
2306
|
|
|
2286
|
-
async def _get_dashboard_search_all_mode(
|
|
2307
|
+
async def _get_dashboard_search_all_mode(
|
|
2308
|
+
self,
|
|
2309
|
+
query: str | None,
|
|
2310
|
+
*,
|
|
2311
|
+
include_screenshot: bool,
|
|
2312
|
+
screenshot_options: _DashboardScreenshotOptions,
|
|
2313
|
+
) -> dict[str, Any]:
|
|
2287
2314
|
"""mode='search': find which storage dashboards contain ``query``.
|
|
2288
2315
|
|
|
2289
2316
|
The component ``search`` walks every storage dashboard in one in-process
|
|
@@ -2325,6 +2352,12 @@ class DashboardConfigTools:
|
|
|
2325
2352
|
f"Results capped at {_SEARCH_ALL_MATCH_CAP} matches; "
|
|
2326
2353
|
"refine the query for a complete list."
|
|
2327
2354
|
]
|
|
2355
|
+
_note_screenshot_ignored(
|
|
2356
|
+
result,
|
|
2357
|
+
include_screenshot=include_screenshot,
|
|
2358
|
+
options=screenshot_options,
|
|
2359
|
+
mode="search",
|
|
2360
|
+
)
|
|
2328
2361
|
return result
|
|
2329
2362
|
|
|
2330
2363
|
async def _search_all_dashboards(
|
|
@@ -2434,6 +2467,7 @@ class DashboardConfigTools:
|
|
|
2434
2467
|
*,
|
|
2435
2468
|
resolved_url_path: list[str | None],
|
|
2436
2469
|
force_reload: bool,
|
|
2470
|
+
view_path: str | None,
|
|
2437
2471
|
include_screenshot: bool,
|
|
2438
2472
|
screenshot_options: _DashboardScreenshotOptions,
|
|
2439
2473
|
) -> "dict[str, Any] | ToolResult":
|
|
@@ -2468,6 +2502,7 @@ class DashboardConfigTools:
|
|
|
2468
2502
|
url_path,
|
|
2469
2503
|
component_config,
|
|
2470
2504
|
original_url_path=url_path,
|
|
2505
|
+
view_path=view_path,
|
|
2471
2506
|
include_screenshot=include_screenshot,
|
|
2472
2507
|
screenshot_options=screenshot_options,
|
|
2473
2508
|
)
|
|
@@ -2518,6 +2553,7 @@ class DashboardConfigTools:
|
|
|
2518
2553
|
url_path,
|
|
2519
2554
|
config,
|
|
2520
2555
|
original_url_path=original_url_path,
|
|
2556
|
+
view_path=view_path,
|
|
2521
2557
|
include_screenshot=include_screenshot,
|
|
2522
2558
|
screenshot_options=screenshot_options,
|
|
2523
2559
|
)
|
|
@@ -2528,6 +2564,7 @@ class DashboardConfigTools:
|
|
|
2528
2564
|
config: Any,
|
|
2529
2565
|
*,
|
|
2530
2566
|
original_url_path: str | None,
|
|
2567
|
+
view_path: str | None,
|
|
2531
2568
|
include_screenshot: bool,
|
|
2532
2569
|
screenshot_options: _DashboardScreenshotOptions,
|
|
2533
2570
|
) -> "dict[str, Any] | ToolResult":
|
|
@@ -2538,6 +2575,14 @@ class DashboardConfigTools:
|
|
|
2538
2575
|
render paths, ``resolved_from`` (when the legacy lazy resolver
|
|
2539
2576
|
canonicalised an internal id), the large-config disclosure hint, and an
|
|
2540
2577
|
optional screenshot.
|
|
2578
|
+
|
|
2579
|
+
``view_path`` scopes the payload to one view: the response carries
|
|
2580
|
+
``view`` + ``view_index`` instead of ``config``. The scoped envelope
|
|
2581
|
+
deliberately has NO ``config`` key so the view object cannot be
|
|
2582
|
+
mistaken for a full config and pushed back through
|
|
2583
|
+
``ha_config_set_dashboard(config=...)``, which would drop every other
|
|
2584
|
+
view; ``config_hash``/``config_size_bytes`` still describe the full
|
|
2585
|
+
config so ``python_transform`` optimistic locking works unchanged.
|
|
2541
2586
|
"""
|
|
2542
2587
|
# Compute hash for optimistic locking in subsequent operations
|
|
2543
2588
|
config_hash = compute_config_hash(config) if isinstance(config, dict) else None
|
|
@@ -2545,19 +2590,37 @@ class DashboardConfigTools:
|
|
|
2545
2590
|
# Calculate config size for progressive disclosure hint
|
|
2546
2591
|
config_size = len(json.dumps(config)) if isinstance(config, dict) else 0
|
|
2547
2592
|
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
get_result
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2593
|
+
if view_path is not None:
|
|
2594
|
+
get_result = self._scoped_view_get_result(
|
|
2595
|
+
url_path,
|
|
2596
|
+
config,
|
|
2597
|
+
view_path=view_path,
|
|
2598
|
+
config_hash=config_hash,
|
|
2599
|
+
config_size=config_size,
|
|
2600
|
+
)
|
|
2601
|
+
else:
|
|
2602
|
+
get_result = {
|
|
2603
|
+
"success": True,
|
|
2604
|
+
"action": "get",
|
|
2605
|
+
"url_path": url_path,
|
|
2606
|
+
"config": config,
|
|
2607
|
+
"config_hash": config_hash,
|
|
2608
|
+
"config_size_bytes": config_size,
|
|
2609
|
+
}
|
|
2610
|
+
_attach_dashboard_render_paths(
|
|
2611
|
+
get_result,
|
|
2612
|
+
url_path,
|
|
2613
|
+
config if isinstance(config, dict) else None,
|
|
2614
|
+
)
|
|
2615
|
+
# Add hint for large configs (progressive disclosure) - 10KB ≈ 2-3k tokens
|
|
2616
|
+
if config_size >= 10000:
|
|
2617
|
+
get_result["hint"] = (
|
|
2618
|
+
f"Large config ({config_size:,} bytes). Pass view_path=... "
|
|
2619
|
+
"to fetch a single view, or use "
|
|
2620
|
+
"ha_config_get_dashboard(entity_id=...) to find card positions, "
|
|
2621
|
+
"then ha_config_set_dashboard(python_transform=...) "
|
|
2622
|
+
"instead of full config replacement."
|
|
2623
|
+
)
|
|
2561
2624
|
# Surface the original caller-passed identifier when the lazy
|
|
2562
2625
|
# resolver canonicalised it (parity with delete_dashboard's
|
|
2563
2626
|
# resolved_id field). Caller can use this to detect that their
|
|
@@ -2565,25 +2628,91 @@ class DashboardConfigTools:
|
|
|
2565
2628
|
if original_url_path is not None and original_url_path != url_path:
|
|
2566
2629
|
get_result["resolved_from"] = original_url_path
|
|
2567
2630
|
|
|
2568
|
-
# Add hint for large configs (progressive disclosure) - 10KB ≈ 2-3k tokens
|
|
2569
|
-
if config_size >= 10000:
|
|
2570
|
-
get_result["hint"] = (
|
|
2571
|
-
f"Large config ({config_size:,} bytes). For edits, use "
|
|
2572
|
-
"ha_config_get_dashboard(entity_id=...) to find card positions, "
|
|
2573
|
-
"then ha_config_set_dashboard(python_transform=...) "
|
|
2574
|
-
"instead of full config replacement."
|
|
2575
|
-
)
|
|
2576
|
-
|
|
2577
2631
|
return await _maybe_attach_screenshot(
|
|
2578
2632
|
get_result,
|
|
2579
2633
|
url_path,
|
|
2580
2634
|
include_screenshot,
|
|
2581
2635
|
client=self._client,
|
|
2582
2636
|
config=config if isinstance(config, dict) else None,
|
|
2583
|
-
|
|
2637
|
+
# view_path doubles as the get-mode scoping parameter now, so a
|
|
2638
|
+
# scoped read without a screenshot request must not trip the
|
|
2639
|
+
# "screenshot render options are ignored" warning.
|
|
2640
|
+
options=(
|
|
2641
|
+
screenshot_options
|
|
2642
|
+
if include_screenshot
|
|
2643
|
+
else replace(screenshot_options, view_path=None)
|
|
2644
|
+
),
|
|
2584
2645
|
raise_on_failure=True,
|
|
2585
2646
|
)
|
|
2586
2647
|
|
|
2648
|
+
def _scoped_view_get_result(
|
|
2649
|
+
self,
|
|
2650
|
+
url_path: str | None,
|
|
2651
|
+
config: Any,
|
|
2652
|
+
*,
|
|
2653
|
+
view_path: str,
|
|
2654
|
+
config_hash: str | None,
|
|
2655
|
+
config_size: int,
|
|
2656
|
+
) -> dict[str, Any]:
|
|
2657
|
+
"""Build the view-scoped get-mode envelope (issue #2010).
|
|
2658
|
+
|
|
2659
|
+
Raises the shared matcher's structured errors for an empty, unknown,
|
|
2660
|
+
or ambiguous ``view_path`` and for strategy dashboards (no static
|
|
2661
|
+
views). Pure config walking — works whether or not the dashboard
|
|
2662
|
+
screenshot beta feature is enabled.
|
|
2663
|
+
"""
|
|
2664
|
+
if not isinstance(config, dict):
|
|
2665
|
+
raise_tool_error(
|
|
2666
|
+
create_error_response(
|
|
2667
|
+
ErrorCode.RESOURCE_NOT_FOUND,
|
|
2668
|
+
"Dashboard config is unavailable, so view_path cannot be resolved.",
|
|
2669
|
+
context={"url_path": url_path, "view_path": view_path},
|
|
2670
|
+
suggestions=["Retry without view_path to inspect the raw response"],
|
|
2671
|
+
)
|
|
2672
|
+
)
|
|
2673
|
+
view_index, view = match_dashboard_view(
|
|
2674
|
+
url_path or "default",
|
|
2675
|
+
config,
|
|
2676
|
+
view_path,
|
|
2677
|
+
strategy_suggestions=(
|
|
2678
|
+
"Omit view_path — strategy dashboards generate their views at "
|
|
2679
|
+
"runtime, so there is no static view config to return",
|
|
2680
|
+
),
|
|
2681
|
+
)
|
|
2682
|
+
views = config.get("views")
|
|
2683
|
+
view_count = len(views) if isinstance(views, list) else 0
|
|
2684
|
+
get_result: dict[str, Any] = {
|
|
2685
|
+
"success": True,
|
|
2686
|
+
"action": "get",
|
|
2687
|
+
"url_path": url_path,
|
|
2688
|
+
"view_path": view_path,
|
|
2689
|
+
"view_index": view_index,
|
|
2690
|
+
"view_count": view_count,
|
|
2691
|
+
"view": view,
|
|
2692
|
+
"config_hash": config_hash,
|
|
2693
|
+
"config_size_bytes": config_size,
|
|
2694
|
+
"view_size_bytes": len(json.dumps(view)),
|
|
2695
|
+
"hint": (
|
|
2696
|
+
f"Scoped to view '{view_path}' — config['views'][{view_index}] "
|
|
2697
|
+
f"of {view_count} views. config_hash and config_size_bytes "
|
|
2698
|
+
"cover the FULL dashboard config, so "
|
|
2699
|
+
"ha_config_set_dashboard(python_transform=...) edits addressed "
|
|
2700
|
+
f"via config['views'][{view_index}] validate as-is. Do NOT "
|
|
2701
|
+
"pass this view object as the config parameter — that would "
|
|
2702
|
+
"replace the entire dashboard. Omit view_path for the full "
|
|
2703
|
+
"config."
|
|
2704
|
+
),
|
|
2705
|
+
}
|
|
2706
|
+
_attach_dashboard_render_paths(get_result, url_path, config)
|
|
2707
|
+
render_paths = get_result.get("render_paths")
|
|
2708
|
+
if isinstance(render_paths, list):
|
|
2709
|
+
get_result["render_paths"] = [
|
|
2710
|
+
row
|
|
2711
|
+
for row in render_paths
|
|
2712
|
+
if isinstance(row, dict) and row.get("view_index") == view_index
|
|
2713
|
+
]
|
|
2714
|
+
return get_result
|
|
2715
|
+
|
|
2587
2716
|
@tool(
|
|
2588
2717
|
name="ha_config_set_dashboard",
|
|
2589
2718
|
tags={"Dashboards"},
|
|
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.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/supervisor_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_client.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/client/websocket_listener.py
RENAMED
|
File without changes
|
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/__init__.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/capture.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/src/ha_mcp/dashboard_screenshot/content.py
RENAMED
|
File without changes
|
{ha_mcp_dev-7.14.2.dev2093 → ha_mcp_dev-7.14.2.dev2095}/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
|