ha-mcp-dev 7.13.0.dev2037__tar.gz → 7.13.0.dev2042__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.13.0.dev2037/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.13.0.dev2042}/PKG-INFO +1 -1
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/pyproject.toml +1 -10
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_deep.py +12 -12
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_fetch.py +36 -14
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_scenes.py +6 -4
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_automations.py +46 -33
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_scenes.py +473 -386
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_scripts.py +203 -145
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_dev.py +242 -151
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_system.py +563 -401
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042/src/ha_mcp_dev.egg-info}/PKG-INFO +1 -1
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/LICENSE +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/README.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/setup.cfg +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/rest_client.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/client/websocket_listener.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/config.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/policy/value_sources.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/server.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/README.md +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/de.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/en.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/locales/ru.json +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/settings_ui/settings.js +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/enhanced.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_dashboards.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_search.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_utility.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/domain_handlers.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/fuzzy_search.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/operation_manager.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/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.13.0.
|
|
7
|
+
version = "7.13.0.dev2042"
|
|
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
|
|
@@ -164,15 +164,6 @@ ignore = [
|
|
|
164
164
|
[tool.ruff.lint.per-file-ignores]
|
|
165
165
|
"__init__.py" = ["F401"]
|
|
166
166
|
"tests/**/*" = ["E501", "B011"]
|
|
167
|
-
# C901 (mccabe complexity) grandfathered debt: newly enabled rule, these files
|
|
168
|
-
# already exceeded the threshold. Remove a line once its functions are
|
|
169
|
-
# refactored below threshold -- do not add new files here.
|
|
170
|
-
"src/ha_mcp/tools/tools_config_automations.py" = ["C901"]
|
|
171
|
-
"src/ha_mcp/tools/tools_config_scenes.py" = ["C901"]
|
|
172
|
-
"src/ha_mcp/tools/tools_config_scripts.py" = ["C901"]
|
|
173
|
-
"src/ha_mcp/tools/tools_dev.py" = ["C901"]
|
|
174
|
-
"src/ha_mcp/tools/tools_system.py" = ["C901"]
|
|
175
|
-
"tests/src/unit/test_advanced_settings_coverage.py" = ["C901"]
|
|
176
167
|
|
|
177
168
|
[tool.pytest.ini_options]
|
|
178
169
|
testpaths = ["tests"]
|
{ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_deep.py
RENAMED
|
@@ -393,12 +393,12 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
393
393
|
failed_count = 0
|
|
394
394
|
yaml_skipped_count = 0
|
|
395
395
|
timeout_count = 0
|
|
396
|
-
# One representative summary —
|
|
397
|
-
#
|
|
398
|
-
#
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
# ``
|
|
396
|
+
# One representative summary — ``record_first_failure`` (which holds
|
|
397
|
+
# the guard) keeps the first ``failed``-class exception, upgrading
|
|
398
|
+
# once to the first HTTP 500 so a fast-failing outlier can't suppress
|
|
399
|
+
# the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
|
|
400
|
+
# (#1784 follow-up). The remaining failures are counted
|
|
401
|
+
# (``failed_count``) but not summarized.
|
|
402
402
|
failed_errors: list[str] = []
|
|
403
403
|
if not bulk_fetched:
|
|
404
404
|
uids_to_fetch = [
|
|
@@ -547,12 +547,12 @@ class DeepSearchMixin(SceneSearchMixin):
|
|
|
547
547
|
failed_count = 0
|
|
548
548
|
yaml_skipped_count = 0
|
|
549
549
|
timeout_count = 0
|
|
550
|
-
# One representative summary —
|
|
551
|
-
#
|
|
552
|
-
#
|
|
553
|
-
#
|
|
554
|
-
#
|
|
555
|
-
# ``
|
|
550
|
+
# One representative summary — ``record_first_failure`` (which holds
|
|
551
|
+
# the guard) keeps the first ``failed``-class exception, upgrading
|
|
552
|
+
# once to the first HTTP 500 so a fast-failing outlier can't suppress
|
|
553
|
+
# the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
|
|
554
|
+
# (#1784 follow-up). The remaining failures are counted
|
|
555
|
+
# (``failed_count``) but not summarized.
|
|
556
556
|
failed_errors: list[str] = []
|
|
557
557
|
if not bulk_fetched:
|
|
558
558
|
sids_to_fetch = [
|
{ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_fetch.py
RENAMED
|
@@ -40,7 +40,8 @@ def summarize_fetch_error(exc: BaseException) -> str:
|
|
|
40
40
|
|
|
41
41
|
HTTP errors render as ``HTTP <code>: <first line of the message>`` (the
|
|
42
42
|
client's own ``API error: <code> - `` prefix is stripped rather than
|
|
43
|
-
duplicated); everything else as ``<ExceptionType>: <first line>``.
|
|
43
|
+
duplicated); everything else as ``<ExceptionType>: <first line>``. An
|
|
44
|
+
empty message renders the bare prefix (``HTTP <code>`` / ``<Type>``).
|
|
44
45
|
"""
|
|
45
46
|
lines = str(exc).strip().splitlines()
|
|
46
47
|
text = lines[0].strip() if lines else ""
|
|
@@ -55,15 +56,24 @@ def summarize_fetch_error(exc: BaseException) -> str:
|
|
|
55
56
|
return summary
|
|
56
57
|
|
|
57
58
|
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
|
|
59
|
+
# Matches a rendered ``summarize_fetch_error`` HTTP-500 summary — ``HTTP
|
|
60
|
+
# 500: <text>``, or bare ``HTTP 500`` when the error body was empty. An HTTP
|
|
61
|
+
# 500 is the one sample whose cause the summary cannot name (the body is
|
|
62
|
+
# aiohttp's generic "500 Internal Server Error" placeholder; the real error —
|
|
63
|
+
# most often a ``!secret`` reference the per-id config view rejects — lives
|
|
64
|
+
# only in the HA log). Shared by the sample upgrade in
|
|
65
|
+
# ``record_first_failure`` and by ``http_500_diagnosis_hint`` so the two can
|
|
66
|
+
# never disagree on what counts as a 500.
|
|
67
|
+
_HTTP_500_SAMPLE = re.compile(r"HTTP 500(?::|$)")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _is_http_500_sample(sample: str) -> bool:
|
|
71
|
+
"""True when ``sample`` is a rendered HTTP-500 summary (``_HTTP_500_SAMPLE``)."""
|
|
72
|
+
return _HTTP_500_SAMPLE.match(sample) is not None
|
|
73
|
+
|
|
64
74
|
|
|
65
75
|
# Static diagnosis appended to an HTTP-500 failed fragment. The sample names
|
|
66
|
-
# the status but not the cause (see ``
|
|
76
|
+
# the status but not the cause (see ``_HTTP_500_SAMPLE``); this points the
|
|
67
77
|
# reader at the HA log and the single most common cause, delivering the
|
|
68
78
|
# five-minute diagnosis the #1784 follow-up asked for without claiming a
|
|
69
79
|
# cause the response can't actually confirm. Kept endpoint-agnostic — this
|
|
@@ -84,23 +94,35 @@ def http_500_diagnosis_hint(failed_sample: str | None) -> str:
|
|
|
84
94
|
Returns ``""`` for any other sample (or ``None``), so non-500 fragments
|
|
85
95
|
are byte-identical to before. See ``_HTTP_500_DIAGNOSIS``.
|
|
86
96
|
"""
|
|
87
|
-
if failed_sample and failed_sample
|
|
97
|
+
if failed_sample and _is_http_500_sample(failed_sample):
|
|
88
98
|
return _HTTP_500_DIAGNOSIS
|
|
89
99
|
return ""
|
|
90
100
|
|
|
91
101
|
|
|
92
102
|
def record_first_failure(failed_errors: list[str], exc: BaseException) -> None:
|
|
93
|
-
"""Capture ``exc``'s summary as the representative ``failed`` sample
|
|
103
|
+
"""Capture ``exc``'s summary as the representative ``failed`` sample.
|
|
94
104
|
|
|
95
|
-
|
|
105
|
+
The first generic-``failed`` exception per fetch pass is summarized
|
|
96
106
|
(subsequent ones are counted but not summarized) — in the motivating
|
|
97
107
|
"every per-id fetch 500s" case that is N-1 fewer ``summarize_fetch_error``
|
|
98
|
-
calls.
|
|
99
|
-
|
|
100
|
-
|
|
108
|
+
calls. One exception: the first HTTP 500 upgrades a non-500 sample, once —
|
|
109
|
+
failure order tracks latency under ``asyncio.gather``, so a fast-failing
|
|
110
|
+
outlier (e.g. a connection blip losing the race against slower 500
|
|
111
|
+
round-trips) must not claim the slot and suppress the
|
|
112
|
+
``http_500_diagnosis_hint`` diagnosis this sample exists to deliver. At
|
|
113
|
+
most two summaries are rendered per pass. Called from the per-id fetch
|
|
114
|
+
closures, which run under ``asyncio.gather`` but never suspend between
|
|
115
|
+
the check and the write, so no lock is needed.
|
|
101
116
|
"""
|
|
102
117
|
if not failed_errors:
|
|
103
118
|
failed_errors.append(summarize_fetch_error(exc))
|
|
119
|
+
return
|
|
120
|
+
if (
|
|
121
|
+
isinstance(exc, HomeAssistantAPIError)
|
|
122
|
+
and exc.status_code == 500
|
|
123
|
+
and not _is_http_500_sample(failed_errors[0])
|
|
124
|
+
):
|
|
125
|
+
failed_errors[0] = summarize_fetch_error(exc)
|
|
104
126
|
|
|
105
127
|
|
|
106
128
|
def is_timeout_error(exc: BaseException) -> bool:
|
{ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/smart_search/_scenes.py
RENAMED
|
@@ -273,10 +273,12 @@ class SceneSearchMixin(ConfigFetchMixin):
|
|
|
273
273
|
skipped_count = 0
|
|
274
274
|
integration_skipped = 0
|
|
275
275
|
timeout_count = 0
|
|
276
|
-
# One representative summary —
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
#
|
|
276
|
+
# One representative summary — ``record_first_failure`` (which holds
|
|
277
|
+
# the guard) keeps the first ``failed``-class exception, upgrading
|
|
278
|
+
# once to the first HTTP 500 so a fast-failing outlier can't suppress
|
|
279
|
+
# the 500 diagnosis hint; it rides partial_reason as an ``e.g.``
|
|
280
|
+
# (#1784 follow-up). The remaining failures are counted
|
|
281
|
+
# (``failed_count``) but not summarized.
|
|
280
282
|
failed_errors: list[str] = []
|
|
281
283
|
|
|
282
284
|
# Attempt C: parallel per-id fetch with a wall-clock budget so a few
|
{ha_mcp_dev-7.13.0.dev2037 → ha_mcp_dev-7.13.0.dev2042}/src/ha_mcp/tools/tools_config_automations.py
RENAMED
|
@@ -839,39 +839,7 @@ class AutomationConfigTools:
|
|
|
839
839
|
and e.status_code == 404
|
|
840
840
|
):
|
|
841
841
|
await self._raise_automation_not_found(identifier)
|
|
842
|
-
|
|
843
|
-
suggestions = [
|
|
844
|
-
"Check automation configuration format",
|
|
845
|
-
"Ensure required fields: alias, triggers, actions",
|
|
846
|
-
"Use entity_id format: automation.morning_routine or unique_id",
|
|
847
|
-
"Use ha_search(domain_filter='automation') to find automations",
|
|
848
|
-
"Use ha_get_skill_guide for automation examples",
|
|
849
|
-
]
|
|
850
|
-
if isinstance(e, HomeAssistantAPIError):
|
|
851
|
-
if "'service'" in error_text and "not allowed" in error_text:
|
|
852
|
-
suggestions.insert(
|
|
853
|
-
0,
|
|
854
|
-
"Use 'action:' not 'service:' for service calls in action steps "
|
|
855
|
-
"(renamed in HA 2024.8).",
|
|
856
|
-
)
|
|
857
|
-
elif "unexpected keyword argument" in error_text.lower():
|
|
858
|
-
suggestions.insert(
|
|
859
|
-
0,
|
|
860
|
-
"An action step contains a field that belongs at the automation root "
|
|
861
|
-
"(e.g. alias, trigger, condition). Each action step should only contain "
|
|
862
|
-
"action/target/data/delay/choose/if/repeat/parallel keys.",
|
|
863
|
-
)
|
|
864
|
-
elif "'variables'" in error_text and "dictionary" in error_text:
|
|
865
|
-
suggestions.insert(
|
|
866
|
-
0,
|
|
867
|
-
"variables must be a dict mapping names to values, "
|
|
868
|
-
'e.g. {"variables": {"my_var": 42}}',
|
|
869
|
-
)
|
|
870
|
-
if bp_warnings:
|
|
871
|
-
suggestions.append(
|
|
872
|
-
"Config had best-practice issues that may be related: "
|
|
873
|
-
+ "; ".join(bp_warnings)
|
|
874
|
-
)
|
|
842
|
+
suggestions = self._build_set_automation_suggestions(e, bp_warnings)
|
|
875
843
|
error = exception_to_structured_error(
|
|
876
844
|
e,
|
|
877
845
|
context={"identifier": identifier},
|
|
@@ -881,6 +849,51 @@ class AutomationConfigTools:
|
|
|
881
849
|
augment_error_dict_with_skill_content(error, bp_warnings)
|
|
882
850
|
raise_tool_error(error)
|
|
883
851
|
|
|
852
|
+
@staticmethod
|
|
853
|
+
def _build_set_automation_suggestions(
|
|
854
|
+
e: Exception, bp_warnings: BestPracticeCheckResult
|
|
855
|
+
) -> list[str]:
|
|
856
|
+
"""Build the ordered suggestion list for a failed config-replacement set.
|
|
857
|
+
|
|
858
|
+
Extracted verbatim from ``ha_config_set_automation``'s exception
|
|
859
|
+
handler: HA-API messages get a targeted lead suggestion inserted, and
|
|
860
|
+
any best-practice warnings are appended.
|
|
861
|
+
"""
|
|
862
|
+
error_text = str(e)
|
|
863
|
+
suggestions = [
|
|
864
|
+
"Check automation configuration format",
|
|
865
|
+
"Ensure required fields: alias, triggers, actions",
|
|
866
|
+
"Use entity_id format: automation.morning_routine or unique_id",
|
|
867
|
+
"Use ha_search(domain_filter='automation') to find automations",
|
|
868
|
+
"Use ha_get_skill_guide for automation examples",
|
|
869
|
+
]
|
|
870
|
+
if isinstance(e, HomeAssistantAPIError):
|
|
871
|
+
if "'service'" in error_text and "not allowed" in error_text:
|
|
872
|
+
suggestions.insert(
|
|
873
|
+
0,
|
|
874
|
+
"Use 'action:' not 'service:' for service calls in action steps "
|
|
875
|
+
"(renamed in HA 2024.8).",
|
|
876
|
+
)
|
|
877
|
+
elif "unexpected keyword argument" in error_text.lower():
|
|
878
|
+
suggestions.insert(
|
|
879
|
+
0,
|
|
880
|
+
"An action step contains a field that belongs at the automation root "
|
|
881
|
+
"(e.g. alias, trigger, condition). Each action step should only contain "
|
|
882
|
+
"action/target/data/delay/choose/if/repeat/parallel keys.",
|
|
883
|
+
)
|
|
884
|
+
elif "'variables'" in error_text and "dictionary" in error_text:
|
|
885
|
+
suggestions.insert(
|
|
886
|
+
0,
|
|
887
|
+
"variables must be a dict mapping names to values, "
|
|
888
|
+
'e.g. {"variables": {"my_var": 42}}',
|
|
889
|
+
)
|
|
890
|
+
if bp_warnings:
|
|
891
|
+
suggestions.append(
|
|
892
|
+
"Config had best-practice issues that may be related: "
|
|
893
|
+
+ "; ".join(bp_warnings)
|
|
894
|
+
)
|
|
895
|
+
return suggestions
|
|
896
|
+
|
|
884
897
|
async def _upsert_automation(
|
|
885
898
|
self,
|
|
886
899
|
config: dict[str, Any],
|