ha-mcp-dev 7.14.2.dev2162__tar.gz → 7.14.2.dev2167__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.dev2162/src/ha_mcp_dev.egg-info → ha_mcp_dev-7.14.2.dev2167}/PKG-INFO +2 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/README.md +1 -1
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/pyproject.toml +1 -1
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/__init__.py +1 -1
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/rest_client.py +0 -77
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/websocket_listener.py +0 -123
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/config.py +2 -32
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/value_sources.py +0 -4
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/server.py +4 -54
- ha_mcp_dev-7.14.2.dev2167/src/ha_mcp/settings_ui/locales/README.md +96 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/de.json +0 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/en.json +0 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/fr.json +0 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/ru.json +0 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/zh-Hans.json +0 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.js +4 -5
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_deep.py +2 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_dashboards.py +1 -51
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_search.py +8 -8
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_utility.py +2 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/domain_handlers.py +0 -172
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/fuzzy_search.py +0 -40
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/operation_manager.py +0 -60
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167/src/ha_mcp_dev.egg-info}/PKG-INFO +2 -2
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/SOURCES.txt +0 -1
- ha_mcp_dev-7.14.2.dev2162/src/ha_mcp/settings_ui/locales/README.md +0 -18
- ha_mcp_dev-7.14.2.dev2162/src/ha_mcp/tools/enhanced.py +0 -190
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/MANIFEST.in +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/setup.cfg +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/__main__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/_pypi_marker +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/_version.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/consent_form.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/auth/provider.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/backup_manager.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/browser_landing.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/supervisor_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/websocket_client.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/capture.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/content.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/paths.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/provision.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/dashboard_screenshot/theme_guard.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/errors.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/llm_exposure.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/approval_queue.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/evaluator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/handlers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/policy/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/py.typed +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/read_only.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude/settings.json +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude-plugin/marketplace.json +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.claude-plugin/plugin.json +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.github/ISSUE_TEMPLATE/skill-rca.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/.github/pull_request_template.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/AGENTS.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/CLAUDE.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/CONTRIBUTING.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/LICENSE +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/README.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/SKILL.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/evals/evals.json +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/appdaemon.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/automation-patterns.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/blueprint-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-cards.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/dashboard-guide.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/device-control.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/domain-docs.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/examples.yaml +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/helper-selection.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/safe-refactoring.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/scenes.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/template-guidelines.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/resources/skills-vendor/skills/home-assistant-best-practices/references/yaml-only-integrations.md +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_advanced.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_backups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_fs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_server.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_handlers_tools.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_i18n.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_supervisor.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_theme.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/_tools_meta.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/es.json +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.css +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/settings.html +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/smoke_test.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/stdio_settings_sidecar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/strict_bps.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/auto_backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/backup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/best_practice_checker.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_api.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_config_reads.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_devices.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_registries.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/component_registry_lookup.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/config_entry_flow.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/device_control.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/matter.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/thread.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/zigbee.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/radio/zwave.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/reference_validator.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_base.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_fetch.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_overview.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/smart_search/_scoring.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tool_search_hint_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_addons.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_areas.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_blueprints.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_bug_report.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_calendar.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_camera.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_categories.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_code.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_automations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_scenes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_config_scripts.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_dashboard_screenshot.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_dev.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_energy.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_entities.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_filesystem.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_groups.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_hacs.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_history.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_integrations.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_labels.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_radio.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_registry.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_resources.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_service.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_services.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_system.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_themes.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_todo.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_traces.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_updates.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_voice_assistant.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_yaml_config.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_yaml_read.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/tools_zones.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/util_helpers.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/tools/validation_middleware.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/categorized_search.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transforms/lite_docstrings.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/transport_security.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/update_check.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/config_hash.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/config_write_lock.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/data_paths.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/kill_signal_diagnostics.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/python_sandbox.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/skill_loader.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/utils/usage_logger.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/enforcement.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/model.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/persistence.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/visibility/resolver.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/dependency_links.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/entry_points.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/requires.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp_dev.egg-info/top_level.txt +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/tests/__init__.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/tests/test_constants.py +0 -0
- {ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/tests/test_env_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ha-mcp-dev
|
|
3
|
-
Version: 7.14.2.
|
|
3
|
+
Version: 7.14.2.dev2167
|
|
4
4
|
Summary: Home Assistant MCP Server - Complete control of Home Assistant through MCP
|
|
5
5
|
Author-email: Julien <github@qc-h.net>
|
|
6
6
|
License: MIT
|
|
@@ -370,7 +370,7 @@ If your model can't see the tools or your Home Assistant, it may be getting hand
|
|
|
370
370
|
|
|
371
371
|
### Enable search-based discovery
|
|
372
372
|
|
|
373
|
-
Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (
|
|
373
|
+
Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search, ha_get_overview, ha_report_issue, etc.). All tools remain callable directly by name once discovered:
|
|
374
374
|
|
|
375
375
|
| Tool | Purpose |
|
|
376
376
|
|------|---------|
|
|
@@ -337,7 +337,7 @@ If your model can't see the tools or your Home Assistant, it may be getting hand
|
|
|
337
337
|
|
|
338
338
|
### Enable search-based discovery
|
|
339
339
|
|
|
340
|
-
Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (
|
|
340
|
+
Set ENABLE_TOOL_SEARCH=true (or toggle the option in the HA add-on). The full catalog is replaced in the tool list with four entry points plus a small set of always-visible "pinned" tools (ha_search, ha_get_overview, ha_report_issue, etc.). All tools remain callable directly by name once discovered:
|
|
341
341
|
|
|
342
342
|
| Tool | Purpose |
|
|
343
343
|
|------|---------|
|
|
@@ -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.dev2167"
|
|
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
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Home Assistant MCP Server
|
|
3
3
|
|
|
4
4
|
A Model Context Protocol server that provides complete control over Home Assistant
|
|
5
|
-
through REST API and WebSocket integration
|
|
5
|
+
through REST API and WebSocket integration.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from ._version import get_version
|
|
@@ -412,28 +412,6 @@ class HomeAssistantClient:
|
|
|
412
412
|
logger.debug(f"Fetching state for entity: {entity_id}")
|
|
413
413
|
return await self._request("GET", f"/states/{entity_id}")
|
|
414
414
|
|
|
415
|
-
async def set_entity_state(
|
|
416
|
-
self, entity_id: str, state: str, attributes: dict[str, Any] | None = None
|
|
417
|
-
) -> dict[str, Any]:
|
|
418
|
-
"""
|
|
419
|
-
Set entity state.
|
|
420
|
-
|
|
421
|
-
Args:
|
|
422
|
-
entity_id: Entity ID
|
|
423
|
-
state: New state value
|
|
424
|
-
attributes: Optional attributes dictionary
|
|
425
|
-
|
|
426
|
-
Returns:
|
|
427
|
-
Updated entity state
|
|
428
|
-
"""
|
|
429
|
-
logger.debug(f"Setting state for entity {entity_id} to {state}")
|
|
430
|
-
|
|
431
|
-
payload: dict[str, Any] = {"state": state}
|
|
432
|
-
if attributes:
|
|
433
|
-
payload["attributes"] = attributes
|
|
434
|
-
|
|
435
|
-
return await self._request("POST", f"/states/{entity_id}", json=payload)
|
|
436
|
-
|
|
437
415
|
async def call_service(
|
|
438
416
|
self,
|
|
439
417
|
domain: str,
|
|
@@ -490,41 +468,6 @@ class HomeAssistantClient:
|
|
|
490
468
|
logger.debug("Fetching available services")
|
|
491
469
|
return await self._request("GET", "/services")
|
|
492
470
|
|
|
493
|
-
async def get_history(
|
|
494
|
-
self,
|
|
495
|
-
entity_id: str | None = None,
|
|
496
|
-
start_time: str | None = None,
|
|
497
|
-
end_time: str | None = None,
|
|
498
|
-
) -> list[list[dict[str, Any]]]:
|
|
499
|
-
"""
|
|
500
|
-
Get historical data.
|
|
501
|
-
|
|
502
|
-
Args:
|
|
503
|
-
entity_id: Optional entity ID to filter
|
|
504
|
-
start_time: Optional start time (ISO format)
|
|
505
|
-
end_time: Optional end time (ISO format)
|
|
506
|
-
|
|
507
|
-
Returns:
|
|
508
|
-
Historical data
|
|
509
|
-
"""
|
|
510
|
-
logger.debug(f"Fetching history for entity: {entity_id}")
|
|
511
|
-
|
|
512
|
-
params = {}
|
|
513
|
-
if start_time:
|
|
514
|
-
params["start_time"] = start_time
|
|
515
|
-
if end_time:
|
|
516
|
-
params["end_time"] = end_time
|
|
517
|
-
|
|
518
|
-
endpoint = "/history/period"
|
|
519
|
-
if entity_id:
|
|
520
|
-
endpoint += f"/{entity_id}"
|
|
521
|
-
|
|
522
|
-
result = await self._request("GET", endpoint, params=params)
|
|
523
|
-
if isinstance(result, list):
|
|
524
|
-
return result
|
|
525
|
-
else:
|
|
526
|
-
return []
|
|
527
|
-
|
|
528
471
|
async def get_logbook(
|
|
529
472
|
self,
|
|
530
473
|
entity_id: str | None = None,
|
|
@@ -962,15 +905,6 @@ class HomeAssistantClient:
|
|
|
962
905
|
logger.error(f"Failed to connect to Home Assistant: {e}")
|
|
963
906
|
return False, str(e)
|
|
964
907
|
|
|
965
|
-
async def get_system_health(self) -> dict[str, Any]:
|
|
966
|
-
"""Get system health information."""
|
|
967
|
-
logger.debug("Fetching system health")
|
|
968
|
-
try:
|
|
969
|
-
return await self._request("GET", "/system_health/info")
|
|
970
|
-
except HomeAssistantAPIError:
|
|
971
|
-
# System health might not be available in all HA instances
|
|
972
|
-
return {"status": "unknown", "message": "System health not available"}
|
|
973
|
-
|
|
974
908
|
# Automation Configuration Management
|
|
975
909
|
|
|
976
910
|
async def _resolve_automation_id(self, identifier: str) -> str:
|
|
@@ -2097,14 +2031,3 @@ class HomeAssistantClient:
|
|
|
2097
2031
|
status_code=405,
|
|
2098
2032
|
) from e
|
|
2099
2033
|
raise
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
async def create_client() -> HomeAssistantClient:
|
|
2103
|
-
"""Create and return a new Home Assistant client."""
|
|
2104
|
-
return HomeAssistantClient()
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
async def test_connection_with_config() -> tuple[bool, str | None]:
|
|
2108
|
-
"""Test connection using configuration settings."""
|
|
2109
|
-
async with HomeAssistantClient() as client:
|
|
2110
|
-
return await client.test_connection()
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/client/websocket_listener.py
RENAMED
|
@@ -7,7 +7,6 @@ WebSocket events and updates operation status in real-time.
|
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import logging
|
|
10
|
-
from datetime import datetime
|
|
11
10
|
from typing import Any
|
|
12
11
|
|
|
13
12
|
from ..config import get_global_settings
|
|
@@ -29,13 +28,6 @@ class WebSocketListenerService:
|
|
|
29
28
|
self.cleanup_task: asyncio.Task | None = None
|
|
30
29
|
self.running = False
|
|
31
30
|
self._event_loop: asyncio.AbstractEventLoop | None = None
|
|
32
|
-
self.stats: dict[str, Any] = {
|
|
33
|
-
"events_processed": 0,
|
|
34
|
-
"operations_updated": 0,
|
|
35
|
-
"connection_errors": 0,
|
|
36
|
-
"last_event_time": None,
|
|
37
|
-
"start_time": None,
|
|
38
|
-
}
|
|
39
31
|
|
|
40
32
|
async def start(self) -> bool:
|
|
41
33
|
"""Start the WebSocket listener service.
|
|
@@ -64,7 +56,6 @@ class WebSocketListenerService:
|
|
|
64
56
|
self.cleanup_task = asyncio.create_task(self._periodic_cleanup())
|
|
65
57
|
|
|
66
58
|
self.running = True
|
|
67
|
-
self.stats["start_time"] = datetime.now()
|
|
68
59
|
|
|
69
60
|
logger.info("WebSocket listener service started successfully")
|
|
70
61
|
return True
|
|
@@ -141,11 +132,6 @@ class WebSocketListenerService:
|
|
|
141
132
|
entity_id: str | None = None
|
|
142
133
|
new_state: dict[str, Any] | None = None
|
|
143
134
|
try:
|
|
144
|
-
events_processed = self.stats["events_processed"]
|
|
145
|
-
if isinstance(events_processed, int):
|
|
146
|
-
self.stats["events_processed"] = events_processed + 1
|
|
147
|
-
self.stats["last_event_time"] = datetime.now()
|
|
148
|
-
|
|
149
135
|
# Extract event data — fields are nested under ``event["data"]``.
|
|
150
136
|
event_data = event.get("data") or {}
|
|
151
137
|
entity_id = event_data.get("entity_id")
|
|
@@ -164,11 +150,6 @@ class WebSocketListenerService:
|
|
|
164
150
|
# Update pending operations
|
|
165
151
|
updated_ops = update_pending_operations(entity_id, new_state)
|
|
166
152
|
if updated_ops:
|
|
167
|
-
operations_updated = self.stats["operations_updated"]
|
|
168
|
-
if isinstance(operations_updated, int):
|
|
169
|
-
self.stats["operations_updated"] = operations_updated + len(
|
|
170
|
-
updated_ops
|
|
171
|
-
)
|
|
172
153
|
logger.info(f"Updated {len(updated_ops)} operations for {entity_id}")
|
|
173
154
|
|
|
174
155
|
except (RuntimeError, ConnectionError, OSError) as e:
|
|
@@ -198,14 +179,8 @@ class WebSocketListenerService:
|
|
|
198
179
|
ping_success = await self.websocket_client.ping()
|
|
199
180
|
if not ping_success:
|
|
200
181
|
logger.warning("WebSocket ping failed")
|
|
201
|
-
connection_errors = self.stats["connection_errors"]
|
|
202
|
-
if isinstance(connection_errors, int):
|
|
203
|
-
self.stats["connection_errors"] = connection_errors + 1
|
|
204
182
|
else:
|
|
205
183
|
logger.warning("WebSocket connection lost")
|
|
206
|
-
connection_errors = self.stats["connection_errors"]
|
|
207
|
-
if isinstance(connection_errors, int):
|
|
208
|
-
self.stats["connection_errors"] = connection_errors + 1
|
|
209
184
|
|
|
210
185
|
# Try to reconnect
|
|
211
186
|
try:
|
|
@@ -241,67 +216,6 @@ class WebSocketListenerService:
|
|
|
241
216
|
logger.error(f"Cleanup task error: {e}")
|
|
242
217
|
await asyncio.sleep(300)
|
|
243
218
|
|
|
244
|
-
def get_status(self) -> dict[str, Any]:
|
|
245
|
-
"""Get service status and statistics.
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
Dictionary with service status and statistics
|
|
249
|
-
"""
|
|
250
|
-
uptime: float | None = None
|
|
251
|
-
start_time = self.stats["start_time"]
|
|
252
|
-
if isinstance(start_time, datetime):
|
|
253
|
-
uptime = (datetime.now() - start_time).total_seconds()
|
|
254
|
-
|
|
255
|
-
return {
|
|
256
|
-
"running": self.running,
|
|
257
|
-
"websocket_connected": (
|
|
258
|
-
self.websocket_client.is_connected if self.websocket_client else False
|
|
259
|
-
),
|
|
260
|
-
"uptime_seconds": uptime,
|
|
261
|
-
"statistics": {
|
|
262
|
-
**self.stats,
|
|
263
|
-
"last_event_time": (
|
|
264
|
-
self.stats["last_event_time"].isoformat()
|
|
265
|
-
if isinstance(self.stats["last_event_time"], datetime)
|
|
266
|
-
else None
|
|
267
|
-
),
|
|
268
|
-
"start_time": (
|
|
269
|
-
self.stats["start_time"].isoformat()
|
|
270
|
-
if isinstance(self.stats["start_time"], datetime)
|
|
271
|
-
else None
|
|
272
|
-
),
|
|
273
|
-
},
|
|
274
|
-
"operation_summary": self.operation_manager.get_operations_summary(),
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
async def force_reconnect(self) -> bool:
|
|
278
|
-
"""Force a WebSocket reconnection.
|
|
279
|
-
|
|
280
|
-
Returns:
|
|
281
|
-
True if reconnection successful
|
|
282
|
-
"""
|
|
283
|
-
try:
|
|
284
|
-
if self.websocket_client:
|
|
285
|
-
self.websocket_client.remove_event_handler(
|
|
286
|
-
"state_changed", self._handle_state_change
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
self.websocket_client = await get_websocket_client()
|
|
290
|
-
await self.websocket_client.subscribe_events("state_changed")
|
|
291
|
-
self.websocket_client.add_event_handler(
|
|
292
|
-
"state_changed", self._handle_state_change
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
logger.info("Forced WebSocket reconnection successful")
|
|
296
|
-
return True
|
|
297
|
-
|
|
298
|
-
except Exception as e:
|
|
299
|
-
logger.error(f"Forced reconnection failed: {e}")
|
|
300
|
-
connection_errors = self.stats["connection_errors"]
|
|
301
|
-
if isinstance(connection_errors, int):
|
|
302
|
-
self.stats["connection_errors"] = connection_errors + 1
|
|
303
|
-
return False
|
|
304
|
-
|
|
305
219
|
|
|
306
220
|
# Global listener service instance
|
|
307
221
|
_listener_service: WebSocketListenerService | None = None
|
|
@@ -353,40 +267,3 @@ async def stop_websocket_listener() -> None:
|
|
|
353
267
|
if _listener_service:
|
|
354
268
|
await _listener_service.stop()
|
|
355
269
|
_listener_service = None
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
async def get_listener_status() -> dict[str, Any]:
|
|
359
|
-
"""Get WebSocket listener service status."""
|
|
360
|
-
service = await get_listener_service()
|
|
361
|
-
return service.get_status()
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
class WebSocketContextManager:
|
|
365
|
-
"""Context manager for WebSocket listener lifecycle."""
|
|
366
|
-
|
|
367
|
-
def __init__(self) -> None:
|
|
368
|
-
self.service: WebSocketListenerService | None = None
|
|
369
|
-
|
|
370
|
-
async def __aenter__(self) -> WebSocketListenerService:
|
|
371
|
-
"""Start WebSocket listener."""
|
|
372
|
-
service = await get_listener_service()
|
|
373
|
-
success = await service.start()
|
|
374
|
-
if not success:
|
|
375
|
-
raise Exception("Failed to start WebSocket listener")
|
|
376
|
-
self.service = service
|
|
377
|
-
return service
|
|
378
|
-
|
|
379
|
-
async def __aexit__(
|
|
380
|
-
self,
|
|
381
|
-
exc_type: type[BaseException] | None,
|
|
382
|
-
exc_val: BaseException | None,
|
|
383
|
-
exc_tb: object,
|
|
384
|
-
) -> None:
|
|
385
|
-
"""Stop WebSocket listener."""
|
|
386
|
-
if self.service:
|
|
387
|
-
await self.service.stop()
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
def websocket_listener_context() -> WebSocketContextManager:
|
|
391
|
-
"""Create a WebSocket listener context manager."""
|
|
392
|
-
return WebSocketContextManager()
|
|
@@ -63,10 +63,9 @@ class Settings(BaseSettings):
|
|
|
63
63
|
|
|
64
64
|
# Tool configuration
|
|
65
65
|
fuzzy_threshold: int = Field(60, alias="FUZZY_THRESHOLD")
|
|
66
|
-
entity_search_limit: int = Field(20, alias="ENTITY_SEARCH_LIMIT")
|
|
67
66
|
|
|
68
67
|
# Smart-search config-fetch time budgets (seconds). Bound how long
|
|
69
|
-
# ha_search
|
|
68
|
+
# ha_search spends fetching automation/script/scene
|
|
70
69
|
# definitions during the per-id fallback before reporting a partial
|
|
71
70
|
# result. Surfaced in the Advanced settings panel (issue #1538) so
|
|
72
71
|
# add-on users — who cannot set raw env vars — can tune them. Consumed
|
|
@@ -466,11 +465,6 @@ class Settings(BaseSettings):
|
|
|
466
465
|
return int(val)
|
|
467
466
|
return val
|
|
468
467
|
|
|
469
|
-
@property
|
|
470
|
-
def env_file_name(self) -> str:
|
|
471
|
-
"""Get the current environment file name."""
|
|
472
|
-
return os.getenv("HAMCP_ENV_FILE", ".env")
|
|
473
|
-
|
|
474
468
|
@field_validator("homeassistant_url")
|
|
475
469
|
@classmethod
|
|
476
470
|
def validate_homeassistant_url(cls, v: str) -> str:
|
|
@@ -580,28 +574,6 @@ def get_settings() -> Settings:
|
|
|
580
574
|
return Settings() # type: ignore[call-arg]
|
|
581
575
|
|
|
582
576
|
|
|
583
|
-
def validate_settings() -> tuple[bool, str | None]:
|
|
584
|
-
"""
|
|
585
|
-
Validate settings and return (is_valid, error_message).
|
|
586
|
-
|
|
587
|
-
Returns:
|
|
588
|
-
tuple: (True, None) if valid, (False, error_message) if invalid
|
|
589
|
-
"""
|
|
590
|
-
try:
|
|
591
|
-
settings = get_settings()
|
|
592
|
-
|
|
593
|
-
# Additional validation
|
|
594
|
-
if not settings.homeassistant_url:
|
|
595
|
-
return False, "Home Assistant URL is required"
|
|
596
|
-
|
|
597
|
-
if not settings.homeassistant_token:
|
|
598
|
-
return False, "Home Assistant token is required"
|
|
599
|
-
|
|
600
|
-
return True, None
|
|
601
|
-
except Exception as e:
|
|
602
|
-
return False, str(e)
|
|
603
|
-
|
|
604
|
-
|
|
605
577
|
# Runtime-editable feature flags surfaced in the /settings web UI
|
|
606
578
|
# (issue #863). Each entry is (field_name, env_var_name, python_type).
|
|
607
579
|
# The web UI's /api/settings/features GET/POST endpoints iterate this
|
|
@@ -811,7 +783,6 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
811
783
|
AdvancedField("verify_ssl", "HA_VERIFY_SSL", bool, "operations", True),
|
|
812
784
|
# Search & matching.
|
|
813
785
|
AdvancedField("fuzzy_threshold", "FUZZY_THRESHOLD", int, "search", True),
|
|
814
|
-
AdvancedField("entity_search_limit", "ENTITY_SEARCH_LIMIT", int, "search", True),
|
|
815
786
|
# Smart-search config-fetch time budgets (#1538). Restart-required
|
|
816
787
|
# (consumed as import-time constants in smart_search/_config.py).
|
|
817
788
|
AdvancedField(
|
|
@@ -941,12 +912,11 @@ ADVANCED_SETTINGS_FIELDS: tuple[AdvancedField, ...] = (
|
|
|
941
912
|
# Bounds present on the Settings field today (mirrored):
|
|
942
913
|
# fuzzy_threshold (validator 0-100), code_mode_* (Field ge/le).
|
|
943
914
|
# Bounds added purely as UI/POST guardrails (no Field constraint):
|
|
944
|
-
# timeout, max_retries
|
|
915
|
+
# timeout, max_retries.
|
|
945
916
|
_ADVANCED_SETTINGS_BOUNDS: dict[str, tuple[float, float]] = {
|
|
946
917
|
"timeout": (1, 600),
|
|
947
918
|
"max_retries": (0, 20),
|
|
948
919
|
"fuzzy_threshold": (0, 100),
|
|
949
|
-
"entity_search_limit": (1, 1000),
|
|
950
920
|
"automation_config_time_budget": (1.0, 600.0),
|
|
951
921
|
"script_config_time_budget": (1.0, 600.0),
|
|
952
922
|
"scene_config_time_budget": (1.0, 600.0),
|
|
@@ -50,10 +50,6 @@ _CACHE_TTL_SECONDS = 30.0
|
|
|
50
50
|
_cache: dict[str, tuple[float, list[str]]] = {}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def value_source_for(tool_name: str, arg_path: str) -> str | None:
|
|
54
|
-
return VALUE_SOURCE_REGISTRY.get((tool_name, arg_path))
|
|
55
|
-
|
|
56
|
-
|
|
57
53
|
def all_value_sources_for(tool_name: str) -> dict[str, str]:
|
|
58
54
|
"""Return {arg_path: value_source} for one tool — used by the UI to
|
|
59
55
|
decide which paths render as dropdowns vs free-text."""
|
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import logging
|
|
13
13
|
from pathlib import Path
|
|
14
|
-
from typing import TYPE_CHECKING, Annotated, Any, ClassVar
|
|
14
|
+
from typing import TYPE_CHECKING, Annotated, Any, ClassVar
|
|
15
15
|
|
|
16
16
|
import yaml # type: ignore[import-untyped]
|
|
17
17
|
from fastmcp import FastMCP
|
|
@@ -20,9 +20,7 @@ from pydantic import Field
|
|
|
20
20
|
|
|
21
21
|
from .config import _PACKAGE_VERSION, get_global_settings
|
|
22
22
|
from .errors import ErrorCode, create_error_response
|
|
23
|
-
from .tools.enhanced import EnhancedToolsMixin
|
|
24
23
|
from .tools.helpers import raise_tool_error
|
|
25
|
-
from .tools.util_helpers import strip_internal_fields
|
|
26
24
|
from .transforms import DEFAULT_PINNED_TOOLS
|
|
27
25
|
|
|
28
26
|
if TYPE_CHECKING:
|
|
@@ -77,7 +75,7 @@ SERVER_ICONS = [
|
|
|
77
75
|
]
|
|
78
76
|
|
|
79
77
|
|
|
80
|
-
class HomeAssistantSmartMCPServer
|
|
78
|
+
class HomeAssistantSmartMCPServer:
|
|
81
79
|
"""Home Assistant MCP Server with smart tools and fuzzy search.
|
|
82
80
|
|
|
83
81
|
Uses lazy initialization to improve startup time:
|
|
@@ -196,9 +194,6 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
|
|
|
196
194
|
# Register tools
|
|
197
195
|
self.tools_registry.register_all_tools()
|
|
198
196
|
|
|
199
|
-
# Register enhanced tools for first/second interaction success
|
|
200
|
-
self.register_enhanced_tools()
|
|
201
|
-
|
|
202
197
|
# Register bundled skills as MCP resources
|
|
203
198
|
self._register_skills()
|
|
204
199
|
|
|
@@ -1789,51 +1784,6 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
|
|
|
1789
1784
|
)
|
|
1790
1785
|
return response
|
|
1791
1786
|
|
|
1792
|
-
# Helper methods required by EnhancedToolsMixin
|
|
1793
|
-
|
|
1794
|
-
async def smart_entity_search(
|
|
1795
|
-
self, query: str, domain_filter: str | None = None, limit: int = 10
|
|
1796
|
-
) -> dict[str, Any]:
|
|
1797
|
-
"""Bridge method to existing smart search implementation."""
|
|
1798
|
-
return cast(
|
|
1799
|
-
dict[str, Any],
|
|
1800
|
-
await self.smart_tools.smart_entity_search(
|
|
1801
|
-
query=query, limit=limit, include_attributes=False
|
|
1802
|
-
),
|
|
1803
|
-
)
|
|
1804
|
-
|
|
1805
|
-
async def get_entity_state(self, entity_id: str) -> dict[str, Any]:
|
|
1806
|
-
"""Bridge method to existing entity state implementation."""
|
|
1807
|
-
return await self.client.get_entity_state(entity_id)
|
|
1808
|
-
|
|
1809
|
-
async def call_service(
|
|
1810
|
-
self,
|
|
1811
|
-
domain: str,
|
|
1812
|
-
service: str,
|
|
1813
|
-
entity_id: str | None = None,
|
|
1814
|
-
data: dict | None = None,
|
|
1815
|
-
) -> list[dict[str, Any]] | dict[str, Any]:
|
|
1816
|
-
"""Bridge method to existing service call implementation."""
|
|
1817
|
-
service_data = data or {}
|
|
1818
|
-
if entity_id:
|
|
1819
|
-
service_data["entity_id"] = entity_id
|
|
1820
|
-
return await self.client.call_service(domain, service, service_data)
|
|
1821
|
-
|
|
1822
|
-
async def get_entities_by_area(self, area_name: str) -> dict[str, Any]:
|
|
1823
|
-
"""Bridge method to existing area functionality.
|
|
1824
|
-
|
|
1825
|
-
``smart_tools.get_entities_by_area`` enriches per-entity dicts
|
|
1826
|
-
with leading-underscore internals (``_hidden_by`` etc.) so
|
|
1827
|
-
downstream search branches can apply the score penalty without
|
|
1828
|
-
a second registry lookup. Strip them here so this public bridge
|
|
1829
|
-
doesn't leak internals to MCP clients.
|
|
1830
|
-
"""
|
|
1831
|
-
result = await self.smart_tools.get_entities_by_area(
|
|
1832
|
-
area_query=area_name, group_by_domain=True
|
|
1833
|
-
)
|
|
1834
|
-
strip_internal_fields(result)
|
|
1835
|
-
return cast(dict[str, Any], result)
|
|
1836
|
-
|
|
1837
1787
|
async def start(self) -> None:
|
|
1838
1788
|
"""Start the Smart MCP server with async compatibility."""
|
|
1839
1789
|
logger.info(
|
|
@@ -1853,8 +1803,8 @@ class HomeAssistantSmartMCPServer(EnhancedToolsMixin):
|
|
|
1853
1803
|
except Exception as e:
|
|
1854
1804
|
logger.error(f"❌ Error testing connection: {e}")
|
|
1855
1805
|
|
|
1856
|
-
# Log
|
|
1857
|
-
logger.info("🔧
|
|
1806
|
+
# Log successful server initialization
|
|
1807
|
+
logger.info("🔧 Home Assistant MCP tools loaded")
|
|
1858
1808
|
|
|
1859
1809
|
# Run the MCP server with async compatibility
|
|
1860
1810
|
await self.mcp.run_async()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Settings UI translations
|
|
2
|
+
|
|
3
|
+
The settings page discovers every `*.json` catalog in this directory. No Python
|
|
4
|
+
or JavaScript registration is required, and no packaging file needs editing —
|
|
5
|
+
the wheel, sdist and binary declarations all match this directory by pattern.
|
|
6
|
+
|
|
7
|
+
## Adding a language
|
|
8
|
+
|
|
9
|
+
A language ships on **all four** translated surfaces or not at all, and one
|
|
10
|
+
Home Assistant language code names every file:
|
|
11
|
+
|
|
12
|
+
- `src/ha_mcp/settings_ui/locales/<code>.json` (this directory)
|
|
13
|
+
- `custom_components/ha_mcp_tools/translations/<code>.json`
|
|
14
|
+
- `homeassistant-addon/translations/<code>.yaml`
|
|
15
|
+
- `homeassistant-addon-dev/translations/<code>.yaml`
|
|
16
|
+
|
|
17
|
+
Adding only this catalog fails `test_every_locale_ships_on_every_surface`. Also
|
|
18
|
+
add the new code to the locale list in the repository-root `AGENTS.md`
|
|
19
|
+
§ Translations — that list is pinned by
|
|
20
|
+
`test_agents_md_lists_every_shipped_locale`.
|
|
21
|
+
|
|
22
|
+
Only the first of those four files is described below, and the other three
|
|
23
|
+
carry content rules of their own: the component catalog needs every `en.json`
|
|
24
|
+
key with identical `{placeholders}` and no extra ones, and each add-on YAML
|
|
25
|
+
needs a `name` and a `description` for every `schema:` key of *that* flavor's
|
|
26
|
+
`config.yaml`, with nothing left behind for a key the schema no longer
|
|
27
|
+
declares. The two flavors declare different schemas, so neither YAML is a copy
|
|
28
|
+
of the other. The repository-root `AGENTS.md` § Translations states all of
|
|
29
|
+
this; a contributor who writes only this catalog goes red on the other three.
|
|
30
|
+
|
|
31
|
+
**Start from a translated catalog, not from `en.json`.** English for the tool
|
|
32
|
+
titles and descriptions comes from the tool definitions at runtime, so `en.json`
|
|
33
|
+
ships `tools` and `tool_groups` empty; a copy of it is missing both sections
|
|
34
|
+
that this catalog is required to carry.
|
|
35
|
+
|
|
36
|
+
**Read the other surfaces before you word a switch.** Wherever the same English
|
|
37
|
+
text is shipped from more than one catalog, your wording has to be byte-identical
|
|
38
|
+
in all of them. That is not only the add-on-options-versus-settings-UI axis: the
|
|
39
|
+
two add-on flavors describe most of the same options, so a good part of the
|
|
40
|
+
pinned parity is stable-against-dev, with no settings-UI text involved at all.
|
|
41
|
+
Translating one surface at a time is exactly how one option ends up with two
|
|
42
|
+
different sentences.
|
|
43
|
+
|
|
44
|
+
## Catalog sections
|
|
45
|
+
|
|
46
|
+
- `meta.native_name`: language name shown in the selector. It must be
|
|
47
|
+
non-empty, must not repeat English's own name, and must differ from every
|
|
48
|
+
other catalog's — a copied catalog that keeps the name it was copied from
|
|
49
|
+
fails `test_native_names_name_their_own_language`, because the picker would
|
|
50
|
+
then offer one label twice.
|
|
51
|
+
- `meta.dir`: `ltr` or `rtl`. Omitting it means `ltr`; any other value is
|
|
52
|
+
rejected when the catalog loads.
|
|
53
|
+
- `messages`: interface labels, help text, notices, and runtime messages. Keys
|
|
54
|
+
may be omitted — English is the per-key fallback at runtime — but see the
|
|
55
|
+
share limit below before leaving a catalog half-finished.
|
|
56
|
+
- `tool_groups`: one entry per renderable MCP tool tag, keyed by the English
|
|
57
|
+
tag. Not optional, and exact: no key more and none fewer.
|
|
58
|
+
- `tools`: `title` and `description` per tool, keyed by the stable MCP tool
|
|
59
|
+
name. The key set is not optional and exact in the same way; either field on
|
|
60
|
+
its own may be left out, but a missing one counts as untranslated against the
|
|
61
|
+
share limit below.
|
|
62
|
+
|
|
63
|
+
Keep the keys and `{placeholders}` unchanged in every section.
|
|
64
|
+
|
|
65
|
+
`messages` values carry two further rules, both enforced when the catalog
|
|
66
|
+
loads rather than by a named test — breaking one raises a `ValueError` at
|
|
67
|
+
import, so the failure names the file but arrives as a broken test module:
|
|
68
|
+
|
|
69
|
+
- The only inline markup the page can restore is `<code>`, `<strong>`, `</a>`
|
|
70
|
+
and `<a href="#" data-panel-link="...">`, spelled exactly that way. Any other
|
|
71
|
+
tag — `<b>`, `<CODE>`, `<code >` — is rejected.
|
|
72
|
+
- A `data-panel-link` target must be a tab the settings page declares, and a
|
|
73
|
+
translated message must link to the same tabs as its English source, with
|
|
74
|
+
the same multiplicity. The order may differ, so a translation is free to
|
|
75
|
+
reorder two links to suit its grammar.
|
|
76
|
+
|
|
77
|
+
## What CI checks
|
|
78
|
+
|
|
79
|
+
- Every surface carries the same set of language codes.
|
|
80
|
+
- `tool_groups` and `tools` name exactly the renderable groups and tools — a
|
|
81
|
+
tool added to the codebase turns every locale red in the PR that adds it.
|
|
82
|
+
- At most 5% of this catalog's `messages`, and 5% of its `tools` texts, may be
|
|
83
|
+
byte-identical to English or missing outright. Both add-on flavors are held to
|
|
84
|
+
the same 5%; the component catalogs allow 15%, because they carry product
|
|
85
|
+
names as keys of their own. A single tool whose `title` *and* `description` are
|
|
86
|
+
both still English fails by name however small the share.
|
|
87
|
+
- One wording per English string across surfaces, wherever the same English text
|
|
88
|
+
is shipped from more than one catalog. The failure names every group that
|
|
89
|
+
disagrees. Where a group has a settings UI member, that is the wording the
|
|
90
|
+
other surfaces follow today; a group carried only by the two add-on flavors
|
|
91
|
+
has no such member, so there pick one wording and use it in both.
|
|
92
|
+
- The English each translation was written against is hashed in
|
|
93
|
+
`tests/src/unit/locale_source_baseline.json`, so a later edit to an English
|
|
94
|
+
string turns the locales red rather than leaving them silently stale. Adding a
|
|
95
|
+
language does not change any English source, so no baseline regeneration is
|
|
96
|
+
needed for it.
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/de.json
RENAMED
|
@@ -371,8 +371,6 @@
|
|
|
371
371
|
"advanced.verify_ssl.help": "Deaktiviere TLS-Überprüfung nur in vertrauenswürdigen Netzwerken. Neustart erforderlich.",
|
|
372
372
|
"advanced.fuzzy_threshold.label": "Fuzzy-Such-Schwellenwert",
|
|
373
373
|
"advanced.fuzzy_threshold.help": "Niedrigere Werte erlauben lockerere Entitätsabgleichung. Bereich 0-100.",
|
|
374
|
-
"advanced.entity_search_limit.label": "Entitätssuchergebnis-Limit",
|
|
375
|
-
"advanced.entity_search_limit.help": "Maximale Entitäten, die von ha_search_entities zurückgegeben werden. Bereich 1-1000.",
|
|
376
374
|
"advanced.automation_config_time_budget.label": "Automatisierungskonfigurations-Zeitbudget (s)",
|
|
377
375
|
"advanced.automation_config_time_budget.help": "Maximale Zeit, die die Tiefensuche für das Abrufen von Automatisierungskonfigurationen aufwendet. Bereich 1-600. Neustart erforderlich.",
|
|
378
376
|
"advanced.script_config_time_budget.label": "Skriptkonfigurations-Zeitbudget (s)",
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/en.json
RENAMED
|
@@ -371,8 +371,6 @@
|
|
|
371
371
|
"advanced.verify_ssl.help": "Disable TLS verification only on trusted networks. Restart required.",
|
|
372
372
|
"advanced.fuzzy_threshold.label": "Fuzzy-search threshold",
|
|
373
373
|
"advanced.fuzzy_threshold.help": "Lower values allow looser entity matching. Range 0–100.",
|
|
374
|
-
"advanced.entity_search_limit.label": "Entity search result limit",
|
|
375
|
-
"advanced.entity_search_limit.help": "Maximum entities returned by ha_search_entities. Range 1–1000.",
|
|
376
374
|
"advanced.automation_config_time_budget.label": "Automation config time budget (s)",
|
|
377
375
|
"advanced.automation_config_time_budget.help": "Maximum time deep search spends fetching automation configs. Range 1–600. Restart required.",
|
|
378
376
|
"advanced.script_config_time_budget.label": "Script config time budget (s)",
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/fr.json
RENAMED
|
@@ -371,8 +371,6 @@
|
|
|
371
371
|
"advanced.verify_ssl.help": "Désactive la vérification TLS uniquement sur les réseaux de confiance. Redémarrage requis.",
|
|
372
372
|
"advanced.fuzzy_threshold.label": "Seuil de recherche floue",
|
|
373
373
|
"advanced.fuzzy_threshold.help": "Des valeurs plus basses permettent une correspondance d'entités plus lâche. Plage 0–100.",
|
|
374
|
-
"advanced.entity_search_limit.label": "Limite des résultats de recherche d'entités",
|
|
375
|
-
"advanced.entity_search_limit.help": "Nombre maximum d'entités renvoyées par ha_search_entities. Plage 1–1000.",
|
|
376
374
|
"advanced.automation_config_time_budget.label": "Budget de temps de configuration d'automatisation (s)",
|
|
377
375
|
"advanced.automation_config_time_budget.help": "Temps maximum que la recherche approfondie consacre à la récupération des configurations d'automatisation. Plage 1–600. Redémarrage requis.",
|
|
378
376
|
"advanced.script_config_time_budget.label": "Budget de temps de configuration de script (s)",
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/ru.json
RENAMED
|
@@ -371,8 +371,6 @@
|
|
|
371
371
|
"advanced.verify_ssl.help": "Отключайте проверку TLS только в доверенной сети. Нужен перезапуск.",
|
|
372
372
|
"advanced.fuzzy_threshold.label": "Порог нечёткого поиска",
|
|
373
373
|
"advanced.fuzzy_threshold.help": "Меньшее значение даёт более свободное сопоставление сущностей. Диапазон 0–100.",
|
|
374
|
-
"advanced.entity_search_limit.label": "Лимит результатов поиска сущностей",
|
|
375
|
-
"advanced.entity_search_limit.help": "Максимум сущностей, возвращаемых ha_search_entities. Диапазон 1–1000.",
|
|
376
374
|
"advanced.automation_config_time_budget.label": "Бюджет времени конфигураций автоматизаций (с)",
|
|
377
375
|
"advanced.automation_config_time_budget.help": "Максимальное время глубокого поиска для загрузки автоматизаций. 1–600. Нужен перезапуск.",
|
|
378
376
|
"advanced.script_config_time_budget.label": "Бюджет времени конфигураций скриптов (с)",
|
{ha_mcp_dev-7.14.2.dev2162 → ha_mcp_dev-7.14.2.dev2167}/src/ha_mcp/settings_ui/locales/zh-Hans.json
RENAMED
|
@@ -371,8 +371,6 @@
|
|
|
371
371
|
"advanced.verify_ssl.help": "仅在受信任网络上禁用 TLS 验证。需重启。",
|
|
372
372
|
"advanced.fuzzy_threshold.label": "模糊搜索阈值",
|
|
373
373
|
"advanced.fuzzy_threshold.help": "较低的值允许更宽松的实体匹配。范围 0–100。",
|
|
374
|
-
"advanced.entity_search_limit.label": "实体搜索结果上限",
|
|
375
|
-
"advanced.entity_search_limit.help": "ha_search_entities 返回的最大实体数。范围 1–1000。",
|
|
376
374
|
"advanced.automation_config_time_budget.label": "自动化配置时间预算(秒)",
|
|
377
375
|
"advanced.automation_config_time_budget.help": "深度搜索获取自动化配置所花费的最大时间。范围 1–600。需重启。",
|
|
378
376
|
"advanced.script_config_time_budget.label": "脚本配置时间预算(秒)",
|